我现在正在用“C编程绝对初学者指南”(第3版)学习C,并且写了所有字符数组的大小应该等于string length + 1
(字符串终止零长度)。但是这段代码:
#include <stdio.h>
main()
{
char name[4] = "Givi";
printf("%s\n",name);
return 0;
}
输出Givi
而不是Giv
。数组大小为4
,在这种情况下,它应输出Giv
,因为4(字符串长度)+ 1(字符串终止零字符长度)= 5,字符数组大小仅为{{1 }}
为什么我的代码输出4
而不是Givi
?
我正在使用MinGW 4.9.2 SEH进行编译。
答案 0 :(得分:7)
你正在达到被认为是undefined behavior的目标。它现在正在运作,但由于机会,不正确。
在你的情况下,这是因为程序中的内存可能在开始时全部归零。因此,即使你的字符串没有正确终止,它恰好在它之后的内存为零,所以printf
知道何时停止。
+-----------------------+
|G|i|v|i|\0|\0|... |
+-----------------------+
| your | rest of |
| stuff | memory (stack)|
+-----------------------+
其他语言(例如Java)可以防范这种情况。然而,像C这样的语言可以减少手握,这一方面可以提供更大的灵活性,但另一方面,给你更多,更多的方法来拍摄你的脚,有这样的微妙问题。换句话说,如果您的代码编译,这并不意味着它是正确的,并且它现在不会在5分钟或5年内爆炸。
在现实生活中,几乎从来没有这种情况,你的字符串最终可能会被存储在其他东西旁边,这总会最终与你的字符串一起打印出来。你永远不会想要这个。像这样的情况可能会导致崩溃,漏洞和泄露的机密信息。
有关示例,请参见下图。想象一下,您正在使用Web服务器并使用字符串“secret” - 用户的密码或密钥存储在无害字符串旁边:
+-----------------------+
|G|i|v|i|s|e|c|r|e|t |
+-----------------------+
| your | rest of |
| stuff | memory (stack)|
+-----------------------+
每当你输出你认为是“Givi”的东西时,你最终会打印出秘密字符串,这不是你想要的。
答案 1 :(得分:1)
你的书中所说的基本上是正确的,但缺少“至少”这个短语。阵列可以更大。
您已经说明了最小长度要求的原因。那么这个例子告诉你什么呢?这是废话!
它展示的内容称为未定义行为(UB),可能会导致守护进程从printf()
飞出你的鼻子而不是初始化程序。它只是没有被C标准覆盖(好吧,标准实际上说这是UB),所以编译器(和你的库)预计不会正常运行。
对于这种情况,不会显式附加终结符,因此传递给`printf()时字符串没有正确终止。
这不会产生错误的原因可能是某些遗留代码确实利用它来保护一些字节的内存。因此,它不会报告隐式尾随'\0'
终止符不适合的错误,而只是不附加它。默默地截断字符串文字也是一个坏主意。
答案 2 :(得分:1)
最后一个字符后面的字节总是必须为0,否则printf
将不知道字符串何时终止,并且当它们不为0时将尝试访问字节(或char
s)。
正如安德烈所说,显然它恰好发生了,编译器在字符串数据之后至少放置了一个值为0的字节,因此printf
识别出字符串的结尾。
这可能因编译器而异,因此是未定义的行为。
例如,有可能printf
访问您的程序不允许的地址@page
。这会导致崩溃。
答案 3 :(得分:1)
在C文本中,字符串存储为零终止的字符数组。这意味着文本字符串的结尾由特殊字符指示,数字值为零(0),表示字符串的结尾。
因此,用于存储C文本字符串的文本字符数组必须包含每个字符的数组元素以及字符串结尾的附加数组元素。
所有C文本字符串函数(strcpy()
,strcmp()
,strcat()
等)都希望文本字符串的结尾由零值表示。这包括打印或输出文本到屏幕或文件的printf()
系列函数。由于这些函数依赖于查看零值来终止字符串,因此使用C文本字符串时的一个错误源是由于缺少零终止符或将长文本字符串复制到较小的缓冲区而复制太多字符。这种类型的错误称为缓冲区溢出错误。
C编译器会自动为您执行某些类型的调整。例如:
char *pText = "four"; // pointer to a text string constant, compiler automatically adds zero to an additional array element for the constant "four"
char text[] = "four"; // compiler creates a array with 5 elements and puts the characters four in the first four array elements, a value of 0 in the fifth
char text[5] = "four"; // programmer creates array of 5 elements, compiler puts the characters four in the first four array elements, a value of 0 in the fifth
在您提供的示例中,一个好的C编译器应该至少发出一个警告,可能是一个错误。但是,您的编译器看起来正在将字符串截断为数组大小,并且不会添加额外的零字符串终止符。而且你很幸运,因为字符串结束后有一个零值。我想C编译器也有可能添加一个额外的数组元素,但这似乎不太可能。
答案 4 :(得分:0)
以下一行:
char name[4] = "Givi";
可能会发出警告:
string for array of chars is too long
因为行为是 Undefined ,所以仍然可以通过编译器传递它。但是如果你调试,你会看到:
name[0] 'G'
name[1] 'i'
name[2] 'V'
name[3] '\0'
所以输出是
GIV
不如你在问题中提到的那样给予!
我正在使用GCC编译器。
但如果你写这样的话:
char name[4] = "Giv";
编译好!输出是
GIV