Joh*_*ers 2 c gcc undefined-behavior
注意: 我知道读取未初始化的字符串是未定义的行为。这个问题具体是关于 GCC 的实施。
\n我使用的是 GCC 版本 6.2.1,并且我观察到长度大于 100 左右的未初始化字符串被初始化为"". 读取未初始化的字符串是未定义的行为,因此编译器可以""根据需要随意将其设置为,并且当字符串足够长时,GCC 似乎会这样做。当然,我永远不会在生产代码中依赖这种行为 - 我只是好奇这种行为在 GCC 中来自哪里。如果它不在 GCC 代码中的某个地方,那么它不断发生就是一个非常奇怪的巧合。
如果我写下面的程序
\n/* string_initialization.c */\n#include <stdio.h>\n\nint main()\n{\n char short_string[10];\n char long_string[100];\n char long_long_string[1000];\n\n printf("%s\\n", short_string);\n printf("%s\\n", long_string);\n printf("%s\\n", long_long_string);\n\n return(0);\n}\nRun Code Online (Sandbox Code Playgroud)\n并用 GCC 编译并运行它,我得到:
\n$ ./string_initialization\n\xef\xbf\xbdQE\xef\xbf\xbd\n\n\n$\nRun Code Online (Sandbox Code Playgroud)\n(有时第一个字符串也是空的)。这表明如果字符串足够长,GCC 会将其初始化为"",但否则并不总是这样做。
如果我用 GCC 编译以下程序并运行它:
\n#include <stdio.h>\n\nint main()\n{\n char long_string[100];\n int i;\n\n for (i = 0 ; i < 100 ; ++i)\n {\n printf("%d ", long_string[i]);\n }\n printf("\\n");\n\n return(0);\n}\nRun Code Online (Sandbox Code Playgroud)\n然后我得到
\n0 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 \nRun Code Online (Sandbox Code Playgroud)\n所以只是字符串的开头被初始化为0,而不是整个字符串。
我想查看 GCC 源代码以了解策略是什么,但我不太了解该代码库,无法知道在哪里查找。
\n背景:我的计算机科学学生提交了一些作品,其中他们声明字符串的长度为 1000,因为“否则会打印奇怪的符号”。你大概可以猜到原因。我希望能够给他们一个很好的答案,解释为什么会发生这种情况以及为什么他们的“修复”有效。
\n更新:感谢那些提供有用答案的人。我刚刚发现,如果字符串长度为 1000,我的计算机会打印出一个空字符串,但如果字符串长度为 960,则打印出垃圾。请参阅 pts 的答案以获得更好的解释。当然,这一切完全依赖于系统,并不是 GCC 的一部分。
\nshort_string正如其他人之前评论的那样,根据 C 标准,读取未初始化的数据(例如 的元素)是未定义的行为。
如果您对使用 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
Run Code Online (Sandbox Code Playgroud)
相反,s您可能想发出其他 GDB 命令来反汇编_start,并逐条指令地运行它。
为什么你的程序从未初始化的长数组中读取0的数据比从未初始化的短数组中读取的数据多的一个可能的解释是,堆栈在开始_start运行之前(大部分)都是全0,然后_start覆盖了堆栈的一些字节,但长数组的开头位于堆栈中尚未被 覆盖的部分_start,因此它仍然是全部0s。使用调试器来确认。
您可能还对从未初始化的全局数组中读取数据感兴趣。这些数组保证由 C 标准初始化0,这是由 GCC 将它们放入.bss节中实现的。请参阅有关 .bss 部分未零初始化的信息,了解如何.bss初始化。
| 归档时间: |
|
| 查看次数: |
188 次 |
| 最近记录: |