问题在于这first
是一个未初始化的指针变量,这意味着指针具有任意值——它可能指向内存中的任何位置。
如果您非常幸运,它最终会指向未使用的已分配内存,因此将字符串读入其中是可行的。
如果幸运的话,它最终会指向一个未映射的地址,因此将字符串读入其中会导致段错误。
如果你运气不好,它最终会指向一些有效的内存,其中包含其他东西,所以它似乎可以工作,但会覆盖其他数据(或代码),从而导致神秘的难以调试的崩溃、不正确的结果或安全漏洞。
添加int i = 0;
并没有真正改变任何东西,除了“重新掷骰子”。您还可以通过例如更改编译器标志(尤其是在您打开或关闭调试或优化功能时)获得不同的结果。
例如,当您main
输入函数时,堆栈分配的区域可能如下所示:
pointer to return address in the middle of libc
pointer to data segment
0
在您的代码的第一个版本中,您没有初始化任何东西,并first
最终继承了指向数据段的指针的值,因此扫描到它是有效的。在第二个版本中,i
最终继承了指向数据段的指针(并用 覆盖它0
),同时first
最终继承了0
值,因此扫描到它的段错误。
如果您有兴趣了解实际情况,可以查看由-S
标志生成的程序集(或编译器的等效项),或者您可以printf("%p\n", first)
然后查看您获得的地址并找出在那里映射的内容。
但实际上,为什么它不起作用并不重要。它不应该工作,唯一的解决方案是将指针正确初始化为有效的东西(正如 ouah 的回答和其他人解释的那样)。