注意: 我知道读取未初始化的字符串是未定义的behvaiour。这个问题具体是关于GCC的实施。
我正在使用GCC版本6.2.1,我观察到长度大于100左右的未初始化字符串被初始化为""
。读取未初始化的字符串是未定义的行为,因此如果需要,编译器可以自由地将其设置为""
,并且当字符串足够长时,GCC似乎正在执行此操作。当然,我永远不会在生产代码中依赖这种行为 - 我只是对GCC中这种行为的来源感到好奇。如果它不在某个地方的GCC代码中,那么它一直在发生,这是一个非常奇怪的巧合。
如果我写下面的程序
/* string_initialization.c */
#include <stdio.h>
int main()
{
char short_string[10];
char long_string[100];
char long_long_string[1000];
printf("%s\n", short_string);
printf("%s\n", long_string);
printf("%s\n", long_long_string);
return(0);
}
使用GCC编译并运行它,我得到:
$ ./string_initialization
�QE�
$
(有时第一个字符串也是空的)。这表明如果字符串足够长,那么GCC会将其初始化为""
,否则它不会总是这样做。
如果我用GCC编译以下程序并运行它:
#include <stdio.h>
int main()
{
char long_string[100];
int i;
for (i = 0 ; i < 100 ; ++i)
{
printf("%d ", long_string[i]);
}
printf("\n");
return(0);
}
然后我得到
0 0 0 0 0 0 0 0 -1 -75 -16 0 0 0 0 0 -62 0 0 0 0 0 0 0 15 84 -42 -17 -4 127 0 0 14 84 -42 -17 -4 127 0 0 69 109 79 -50 46 127 0 0 1 0 0 0 0 0 0 0 -35 5 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -112 5 64 0 0 0 0 0 80 4 64 0 0 0 0 0 16 85 -42 -17
所以只是字符串的开头被初始化为0
,而不是整个事情。
我想查看GCC源代码,看看政策是什么,但我不太了解代码库,知道在哪里查看。
背景:我的CS学生参与了一些工作,他们宣称字符串的长度为1000,因为其他字符会打印出奇怪的符号&#39;你可能会猜到为什么。我希望能够给他们一个很好的答案,说明为什么会这样,以及为什么他们要修复&#39;工作。
更新:感谢那些提供有用答案的人。我发现如果字符串长度为1000,我的计算机会打印出一个空字符串,但如果字符串长度为960,则会打印出垃圾。请参阅pts的答案以获得一个很好的解释。当然,所有这些都完全取决于系统,并不是GCC的一部分。
答案 0 :(得分:4)
正如其他人之前评论的那样,根据C标准,读取未初始化的数据(例如short_string
的元素)是未定义的行为。
如果您对GCC编译并在Linux上运行时实际发生的事情感兴趣,可以参考以下内容。
main
不是第一个在程序启动时运行的函数。入口点通常称为_start
,并调用main
。当main
运行时,这些未初始化数组中的堆栈上的内容取决于之前放置的内容,即_start
在调用main
之前所执行的操作。 _start
的作用取决于GCC和libc。
要弄清楚究竟发生了什么,你可能想用gcc -static -g
编译你的程序,并在调试器中运行它,如下所示:
$ gcc -static -g -o myprog myprog.c
$ gdb ./myprog
(gdb) b _start
(gdb) run
(gdb) s
您可能希望发出其他GDB命令来获取s
的反汇编,而不是_start
,而是按指令运行它。
一个可能的解释为什么你的程序从未初始化的长数组中读取更多0
而不是从未初始化的短数组读取,可能是在{{1}之前,堆栈(大多数)在开始时(大部分)全部为0 } {}开始运行,然后_start
覆盖了堆栈的一些字节,但是长数组的开头是堆栈的一部分,而_start
还没有覆盖它,所以它仍然是_start
1}}秒。使用调试器进行确认。
您可能也有兴趣从未初始化的全局数组中读取数据。这些数组保证由C标准初始化为0
,这是由GCC将它们放入0
部分实现的。有关.bss
如何初始化的信息,请参见how about .bss section not zero initialized。
答案 1 :(得分:3)
答案很简单,发生这种情况的原因是由于读取未初始化变量值导致的未定义行为
答案 2 :(得分:3)
GCC根本没有初始化这些字符串。您只是看到堆栈恰好包含零,并且您正在想象这是编译器的一些故意行为。它不是。
将结果与http://coliru.stacked-crooked.com/a/38f3e70be871af61进行比较,这表明即使第一次调用函数时数组的前几个字节恰好为零,第二次字节也不为零(因为我做了堆栈脏,编译器没有初始化数组。)
您不能假设某些未定义的行为是可靠的,可重复的或有意的。这是一个非常危险的假设。