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
的示例是非法的,不会编译。显式指定的初始值设定项多于数组中的元素是非法的。例外情况仅适用于字符串端子,其中允许尾随的隐式零“脱落”。