当我们在 C 中的字符串末尾不包含 '\0' 时会发生什么?

What happened when we do not include '\0' at the end of string in C?

在 C 中,当我以这种方式初始化我的数组时:

char full_name[] = {
    't', 'o', 'a', 'n'
};

并用printf("%s", full_name);

打印出来

和运行它与valgrind我得到了错误

Uninitialised value was create by stack allocation

为什么会这样?

如果您没有在末尾为逗号分隔的大括号括起来的初始化列表提供 '[=12=]',从技术上讲,full_name 不是 字符串 ,因为 char 数组不是 null-terminated.

只是为了清楚一点,与初始化程序是字符串文字不同,逗号分隔列表不会自动计数并将终止空字符放入数组中。

所以,如果定义像

char full_name[] = {
    't', 'o', 'a', 'n'
};

数组的大小为 4,其中包含 't''o''a''n'

OTOH,在

的情况下
char full_name[] = "toan";

full_name 的大小为 5,其中包含 't''o''a''n''[=12=]'

当您尝试将前一个数组与在 strings 上运行的任何函数一起使用时(即,需要一个 null-terminated char 数组),您'会得到 undefined behavior 因为大多数字符串函数在搜索 null-terminator.

时会越界

在您的特定示例中,对于带有 printf()%s 格式说明符,引用 C11 标准,第 §7.21.6.1 章,fprintf() 函数描述(强调我的)

s
If no l length modifier is present, the argument shall be a pointer to the initial element of an array of character type.280) Characters from the array are written up to (but not including) the terminating null character. If the precision is specified, no more than that many bytes are written. If the precision is not specified or is greater than the size of the array, the array shall contain a null character.

这意味着,printf() 将查找 null-terminator 到 mark/understand 数组的末尾。在您的示例中,缺少 null-terminator 将导致 printf() 超出分配的内存(full_name[3])并访问 out-of-bound 内存(full_name[4]),这将导致UB。

由于 %s 格式说明符需要一个 null-terminated 字符串,因此代码的结果行为是未定义的。您的程序被认为是 ill-formed,并且可以产生任何输出、不产生输出、崩溃等等。简而言之,不要那样做。

这并不是说所有字符数组都必须 null-terminated:该规则仅适用于打算用作 C 字符串的字符数组,例如传递给 %s 格式说明符上的 printf,或传递给 strlen 或标准 C 库的其他字符串函数。

如果您打算将 char 数组用于其他用途,则不需要以空值终止。例如,这个用法是完全定义的:

char full_name[] = {
    't', 'o', 'a', 'n'
};
for (size_t i = 0 ; i != sizeof(full_name) ; i++) {
    printf("%c", full_name[i]);
}

printf 会将“%s”解释为标准 C 字符串。这意味着生成的代码将继续读取字符,直到找到空终止符 (\0)。

通常这意味着这个游荡的指针会冒险进入未知的内存,而 Valgrind 会注意到这是一个错误。

如果您打算在某个时候将其用作字符串,则必须在初始化 char 数组时显式添加您自己的空终止符。

如果您使用 non-null-terminated 字符序列作为字符串,C 函数将继续运行。是“\0”告诉他们停止。因此,序列之后发生在内存中的任何内容都将被视为字符串的一部分。这最终可能会越过内存边界并导致错误,或者如果它碰巧在某处找到 '\0' 并停止,它可能只会打印乱码。

在将指令指针传递给需要 c 字符串的函数之前,您隐式地输入了与该代码块具有法律约束力的合同。在本合同的主要部分,双方同意避免交换专用的字符串长度信息,并断言声明为字符串的所有传递参数都指向以 [=10=] 结尾的字符序列,这使每一方都可以选择计算长度.

如果您不包含终止条款 [=10=],您将根本违反合同。

OS 法庭将随机起诉您的可执行文件,使其发疯甚至死亡。