C:奇怪的弦现象
有人能解释这种现象吗C:奇怪的弦现象,c,arrays,string,printf,C,Arrays,String,Printf,有人能解释这种现象吗 #include "stdio.h" #include "stdlib.h" int main() { char foo[]="foo"; char bar[3]="bar"; printf("%s",foo); printf("\n"); printf("%s",bar); return 0; } 结果: foo barfoo foo bar foobar FOOfoobar BARFOOfoobar 如果我在fo
#include "stdio.h"
#include "stdlib.h"
int main()
{
char foo[]="foo";
char bar[3]="bar";
printf("%s",foo);
printf("\n");
printf("%s",bar);
return 0;
}
结果:
foo
barfoo
foo
bar
foobar
FOOfoobar
BARFOOfoobar
如果我在foo之前更改顺序并创建bar,我会得到正确的输出
#include "stdio.h"
#include "stdlib.h"
int main()
{
char bar[3]="bar";
char foo[]="foo";
printf("%s",foo);
printf("\n");
printf("%s",bar);
return 0;
}
结果:
foo
barfoo
foo
bar
foobar
FOOfoobar
BARFOOfoobar
还有一个
#include "stdio.h"
#include "stdlib.h"
int main()
{
char foobar[]="foobar";
char FOO[3]={'F','O','O','\0'};
char BAR[3]="BAR";
printf("%s",foobar);
printf("\n");
printf("%s",FOO);
printf("\n");
printf("%s",BAR);
return 0;
}
结果:
foo
barfoo
foo
bar
foobar
FOOfoobar
BARFOOfoobar
您缺少字符串末尾的\0。。并且一个包含4个元素的数组被声明为FOO[4]而不是FOO[3]。正如您在第
行中所暗示的那样,char FOO[3]={F','O','O','\0'}代码>这是一个空终止问题。问题是空终止符是一个字符。如果为3个字符分配内存,则不能将4个字符放在该位置(它只取前3个字符并截断其余字符)。在第一个示例中,您没有以null结尾的字符串。碰巧它们在内存中是连续排列的,因此这种行为可以解释为从一个字符串到另一个字符串的转换
在下一个示例中,FOO
的大小为3,但您给它四个元素。在相同的多个条中
不是以null结尾的
char FOO[3]={'F','O','O','\0'};
char BAR[3]="BAR";
如果您声明charbar[3]=“bar”
,然后您将声明一个char
数组,其中没有空终止符的空间。因此,printf()
将继续从内存中读取char
s,将它们打印到控制台,直到它遇到一个'\0'
字符串“bar”
有四个字符长:{b',a',r','\0'}
。如果明确指定数组长度,则需要分配至少四个字符:
char bar[4]="bar";
执行此操作时:
char bar[3]="bar";
printf("%s",bar);
您正在调用未定义的行为,因为bar
变量没有空终止符。任何事情都有可能发生。在本例中,编译器在内存中连续布置了两个数组:
'b' 'a' 'r' 'f' 'o' 'o' '\0'
^ ^
bar[3] foo[4]
当您打印bar
时,它会一直读取,直到找到一个空终止符,即任何空终止符。由于bar
没有任何字符,它会一直运行,直到它在“foo\0”
bar的末尾找到一个字符为止,因此printf会一直跟随数组,直到到达“\0”字符。堆栈的排列方式使bar和foo在内存中紧挨着。C知道数组大小的唯一方法是找到一个空终端。因此,如果您在内存中布局堆栈,它将如下所示:
0 1 2 3 4 5 6
'b' 'a' 'r' 'f' 'o' 'o' '\0'
^bar begins ^foo begins
通过说foo[],编译器将根据其初始化时使用的常量字符串设置foo的大小。它足够聪明,可以在4个字符中包含空终止符“\0”
要解决此问题,钢筋的尺寸实际上应为4,即:
char bar[4] = "bar"; // extra space for null terminal
或者更好的方法是,让编译器像处理foo一样处理它:
char bar[] = "bar"; // compiler adds null term character('\0')
charbar[3]=“bar”代码>没有留下足够的空间来添加终止的'\0'
字符
如果你做了charbar[4]=“bar”代码>,您应该会得到您期望的结果。请阅读详细的答案,这将为您提供深入了解
你之所以看到“有趣”的东西是因为字符串不是以NUL结尾的 罪魁祸首是这句话:
char bar[3]="bar";
这将导致您创建的长度为3的数组中仅包含“b”、“a”和“r”
现在,当它发生时,foo中的字符串是“f”、“o”、“o”和“\0”,并且它是用bar分配的连续位置。所以记忆看起来像:
b | a | r | f | o | o | \0
我希望这能说明问题。其他海报已经在您的
char bar[3] = "bar";
示例字符串终止符不适合数组,因此字符串以未终止结尾。从形式上讲,它甚至不是一个字符串(因为Sting需要根据定义终止)。您试图将非字符串打印为字符串(使用%s
格式说明符),这会导致未定义的行为。未定义的行为正是你观察到的
在C++语言中(例如)
声明将是非法的,因为C++不允许零终止符在这样的声明中“脱落”。C允许它,但仅适用于隐式零终止符字符。
char bar[3] = "barr";
在C和C++中声明是非法的。
同样,“丢失零”技巧在C中只适用于隐式零终止符字符。它不适用于任何显式初始值设定项:不允许显式指定多于数组中元素数的初始值设定项。这就引出了你的第三个例子。在你的第三个例子中,你有
char FOO[3] = { 'F', 'O', 'O', '\0' };
声明,它为大小为3的数组显式指定4个初始值设定项。这在C中是非法的。您的第三个示例不可编译。如果您的编译器在没有诊断消息的情况下接受了它,则您的编译器必须已损坏。第三个程序的行为不能用C语言解释,因为它不是C程序。这一行
char bar[3]="bar";
导致未定义的行为,因为“bar”
是四个字符,负责“\0”
。因此,您的bar
数组应该是四个字节
未定义的行为意味着任何事情都可能发生-包括好的和坏的事情如果您试图用4字节的数据填充3字节的缓冲区,是否会出现警告?系统头(不是必需的文件)由
标识。头文件由“my_Header.h”
标识(如果引号处理失败,则重新处理为
。@MarkusG-:第三个示例不可编译。FOO
的声明是非法的,任何符合条件的C编译器都将立即拒绝该声明。如果您的编译器在没有诊断消息的情况下接受它,则表明您的编译器已损坏。@Timbo我没有收到任何警告。(使用代码::Blocks 10.05)这不是“奇怪”,也不是“现象”。阅读C语言书中关于字符串的章节。严格来说,带有FOO
的示例是非法的,不会编译。显式指定的初始值设定项多于数组中的元素是非法的。例外情况仅适用于字符串端子,其中允许尾随的隐式零“脱落”。