有人可以解释这种现象。
#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之前创建栏,我会得到正确的输出。
#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
bar
还有一个。
#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;
}
结果:
foobar
FOOfoobar
BARFOOfoobar
答案 0 :(得分:7)
字符串"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"
结尾处找到它之后才会继续运行。
答案 1 :(得分:2)
如果声明char bar[3]="bar";
,那么您将声明一个char
数组,没有空终止符的空间。因此,printf()
将继续从内存中读取char
,将其打印到控制台,直到遇到'\0'
。
答案 2 :(得分:2)
其他海报已经向你解释过你的
char bar[3] = "bar";
示例字符串终止符不适合数组,因此字符串最终不会终止。从形式上讲,它甚至不是一个字符串(因为需要根据定义终止叮咬)。您正在尝试将非字符串作为字符串打印(使用%s
格式说明符),这会导致未定义的行为。未定义的行为正是您所观察到的。
用C ++语言(例如)
char bar[3] = "bar";
声明是非法的,因为C ++不允许零终止符在这样的声明中“脱落”。 C允许它,但仅适用于隐式零终止符。
char bar[3] = "barr";
声明在C和C ++中都是非法的。
同样,“缺少零”技巧仅在C中使用隐式零终止符。它不适用于任何显式初始值设定项:不允许显式指定比数组中的元素更多的初始值设定项。这将我们带到你的第三个例子。在你的第三个例子中,你有
char FOO[3] = { 'F', 'O', 'O', '\0' };
声明,显式为大小为3的数组指定4个初始化器。这在C中是非法的。您的第三个示例是不可编译的。如果编译器在没有诊断消息的情况下接受它,则必须破坏编译器。第三个程序的行为无法用C语言解释,因为它不是C程序。
答案 3 :(得分:1)
当你暗示知道你的行char FOO[3]={'F','O','O','\0'};
时,这是一个空终止问题。问题是null终止符是一个字符。如果为3个字符分配内存,则不能在该位置放置4个字符(只需要前3个字符并截断其余字符)。
答案 4 :(得分:0)
你错过了字符串末尾的\ 0和一个包含4个元素的数组被声明为FOO [4]而不是FOO [3] ..
答案 5 :(得分:0)
在第一个示例中,您没有以空字符结尾的字符串。碰巧它们被连续地放在内存中,因此行为可以解释为从一个字符串到另一个字符串的运行。
在下一个示例中,FOO
的大小为3,但您要给它四个元素。同样,许多BAR
不会以空值终止。
char FOO[3]={'F','O','O','\0'};
char BAR[3]="BAR";
答案 6 :(得分:0)
bar不是以null结尾,因此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'。
要解决此问题,bar的大小实际应为4,即:
char bar[4] = "bar"; // extra space for null terminal
或更好,让编译器像你使用foo一样解决它:
char bar[] = "bar"; // compiler adds null term character('\0')
答案 7 :(得分:0)
char bar[3]="bar";
没有足够的空间来添加终止'\0'
字符。
如果你char bar[4]="bar";
,你应该得到你期望的结果。
答案 8 :(得分:0)
请阅读this详细解答,以便深入了解这一点......
你看到“有趣”的原因是因为字符串不是NUL终止......
答案 9 :(得分:0)
罪魁祸首就是这条线:
char bar[3]="bar";
这会导致'b','a'和'r'位于您创建的长度为3的数组中。
现在碰巧,foo中的字符串是'f','o','o'和'\ 0',并且它被分配了连续的位置和条形码。所以记忆看起来像:
b | a | r | f | o | o | \0
我希望这说清楚。
答案 10 :(得分:0)
这一行
char bar[3]="bar";
导致未定义的行为,因为"bar"
是由'\0'
处理的四个字符。所以你bar
数组应该是四个字节。
未定义的行为意味着任何事情都可能发生 - 包括好事和坏事