gcc是否将长字符串初始化为“""”而不是短字符串?

时间:2016-11-10 16:50:53

标签: c gcc undefined-behavior

注意: 我知道读取未初始化的字符串是未定义的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的一部分。

3 个答案:

答案 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进行比较,这表明即使第一次调用函数时数组的前几个字节恰好为零,第二次字节也不为零(因为我做了堆栈脏,编译器没有初始化数组。)

您不能假设某些未定义的行为是可靠的,可重复的或有意的。这是一个非常危险的假设。