在C中,当我以这种方式初始化我的数组时:
char full_name[] = {
't', 'o', 'a', 'n'
};
并使用printf("%s", full_name);
并使用 valgrind 运行它我收到了错误
未初始化的值是由堆栈分配创建的
为什么会这样?
答案 0 :(得分:6)
如果您没有在逗号分隔括号初始化列表的末尾提供'\0'
,从技术上讲,full_name
不是字符串,因为{{1数组不是以空值终止的。
只是为了清除一点,不像初始化程序是字符串文字,逗号分隔列表不会自动计数并将终止空字符放入数组中。
因此,如果是
这样的定义char
数组的大小为4,它包含char full_name[] = {
't', 'o', 'a', 'n'
};
,'t'
,'o'
,'a'
。
OTOH,如果是
'n'
char full_name[] = "toan";
的大小为5,其中包含full_name
,'t'
,'o'
,'a'
和'n'
。
当您尝试使用在字符串上运行的任何函数的前一个数组时(即,期望一个以null结尾的'\0'
数组),您将获得{{3}因为大多数字符串函数将在搜索null-terminator时超出范围。
在您的特定示例中,对于char
格式说明符%s
,引用printf()
标准,章节§7.21.6.1,C11
函数说明(强调雷)
fprintf()
如果不存在s
长度修饰符,则参数应为指向初始值的指针 字符类型数组的元素。 280) 数组中的字符是 写入(但不包括)终止空字符。如果 指定精度,不超过写入的多个字节。 如果 精度未指定或大于数组的大小,数组应 包含空字符。
这意味着,l
将寻找一个空终止符来标记/理解数组的结尾。在您的示例中,缺少null终止符将导致printf()
超出分配的内存(printf()
)并访问超出范围的内存(full_name[3]
),这将导致UB。
答案 1 :(得分:6)
由于%s
格式说明符需要以空字符结尾的字符串,因此未定义代码的结果行为。您的程序被认为是不正确的,可以产生任何输出,不产生输出,崩溃等等。不久之后,请不要这样做。
这并不是说所有字符数组都必须以空值终止:该规则仅适用于打算用作C字符串的字符数组,例如:要传递给printf
格式说明符上的%s
,或传递给标准C库的strlen
或其他字符串函数。
如果您打算将char
数组用于其他内容,则不需要将其终止。例如,这种用法是完全定义的:
char full_name[] = {
't', 'o', 'a', 'n'
};
for (size_t i = 0 ; i != sizeof(full_name) ; i++) {
printf("%c", full_name[i]);
}
答案 2 :(得分:1)
printf会将“%s”解释为标准C字符串。这意味着生成的代码将只是继续读取字符,直到找到空终止符(\ 0)。
通常这意味着这个流浪的指针会冒险进入未知的内存,而Valgrind会将此视为错误。
如果您打算在某个时候将其用作字符串,则必须在初始化char数组时显式添加自己的null终止符。
答案 3 :(得分:1)
如果使用非空终止的char序列作为字符串,C函数将继续运行。它是'\ 0'告诉他们停止。因此,无论在序列中将序列作为字符串的一部分发生在内存中。这可能最终会跨越内存边界并导致错误,或者如果碰巧在某处找到'\ 0'并停止,则可能只是打印乱码。
答案 4 :(得分:1)
在将指令指针传递给期望c字符串的函数之前,您隐式输入与该代码块具有法律约束力的合同。在本合同的主要部分,双方同意避免交换专用的字符串长度信息,并断言声明为字符串的所有传递参数都指向由\0
终止的字符序列,这使得每一方都可以选择计算长度。
如果您不包含终止\0
,则会违反合同。
操作系统法庭将随意起诉你的可执行文件,甚至死亡。