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

toa*_*ong 1 c string valgrind null-terminated

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

char full_name[] = {
    't', 'o', 'a', 'n'
};
Run Code Online (Sandbox Code Playgroud)

并打印出来 printf("%s", full_name);

valgrind运行它我得到了错误

未初始化的值由堆栈分配创建

为什么会这样?

Sou*_*osh 6

如果你没有提供'\0'逗号分隔括号初始化列表的末尾,从技术上讲,full_name不是字符串,因为char数组不是以空值终止的.

只是为了清除一些东西,不像初始化程序是字符串文字,逗号分隔列表不会自动计数并将终止空字符放入数组中.

所以,如果有像这样的定义

char full_name[] = {
    't', 'o', 'a', 'n'
};
Run Code Online (Sandbox Code Playgroud)

阵列的大小是4,它有't','o','a','n'进去.

OTOH,如果是的话

char full_name[] = "toan";
Run Code Online (Sandbox Code Playgroud)

full_name将是大小为5,将包含't','o''a','n''\0'在其中.

当你尝试使用前一个数组和任何对字符串操作的函数(即,期望一个以null结尾的char数组)时,你将得到未定义的行为,因为大多数字符串函数将超出搜索范围 -终止.

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

s
如果不存在l长度修饰符,则参数应为指向字符类型数组的初始元素的指针.280) 来自数组的字符被写入(但不包括)终止空字符.如果指定了精度,则不会写入多少个字节.如果未指定精度或大于数组的大小,则数组应包含空字符.

这意味着,printf()将寻找一个空终止符来标记/理解数组的结尾.在您的示例中,缺少null终止符将导致printf()超出分配的内存(full_name[3])并访问full_name[4]将导致UB的超出内存().


das*_*ght 6

由于%s格式说明符需要以空字符结尾的字符串,因此未定义代码的结果行为.您的程序被认为是不正确的,可以产生任何输出,不产生输出,崩溃等等.简而言之,不要这样做.

这并不是说,人物的所有阵列必须是空值终止:该规则仅适用于打算作为C字符串,例如用来传递给字符数组printf%s格式说明,或要传递到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]);
}
Run Code Online (Sandbox Code Playgroud)

  • @JohnSensebe C标准要求`sizeof(char)`始终生成`1`. (2认同)
  • 很公平。无论如何,更好的选择是 `i != sizeof(full_name) / sizeof(full_name[0])`,以防万一`full_name` 的定义发生变化。 (2认同)