我有简单的输入功能:
int main(void)
{
char *first;
char *last;
scanf("%s", first);
printf("%s", last);
return 1;
}
但是当我开始扩展我的工作并放置简单的计数器时,程序崩溃了:
int main(void)
{
int i = 0;
char *first;
char *last;
scanf("%s", first);
printf("%s", last);
return 1;
}
任何想法?
答案 0 :(得分:2)
char *first;
scanf("%s", first);
在致电first
之前, scanf
未初始化:呼叫前的值是无效地址。
答案 1 :(得分:1)
您要么在使用char*
存储之前动态分配scanf()
,要么切换到字符数组。
答案 2 :(得分:1)
问题是first
是一个未初始化的指针变量,意味着指针有一个任意值 - 它可能指向内存中的任何位置。
如果你非常幸运,它最终指向未使用的已分配内存,因此读取字符串恰好可以正常工作。
如果你运气好,它最终会指向一个未映射的地址,因此读取一个字符串会导致段错误。
如果你运气不好,它最终会指向一些有效的内存,它会保留其他东西,所以它似乎可以工作,但会覆盖其他数据(或代码),从而导致神秘的难以调试的崩溃,不正确的结果或安全性孔。
添加int i = 0;
并没有真正改变任何东西,除了“重新掷骰子”。您也可以通过更改编译器标志来获得不同的结果(特别是如果打开或关闭调试或优化功能)。
例如,当输入main
函数时,堆栈的分配区域可能是这样的:
pointer to return address in the middle of libc
pointer to data segment
0
在代码的第一个版本中,您没有初始化任何内容,first
最终继承了指向数据段的指针的值,因此扫描到它的工作原理。在第二个版本中,i
最终会继承指向数据段的指针(并使用0
覆盖它),而first
最终会继承0
值,因此扫描进入segfaults。
如果您对查看实际情况感兴趣,可以查看由-S
标志(或编译器的等效标志)生成的程序集,或者只能printf("%p\n", first)
然后查看你得到了什么地址,并弄清楚那里的地图。
但实际上,为什么不起作用并不重要。它不应该工作,唯一的解决方案是正确地将指针初始化为有效的东西(如ouah的回答和其他解释)。
答案 3 :(得分:0)
您的first
变量未初始化。将其声明为数组或根据您的需要动态分配。
数组版本,
int main(void)
{
char first[ SIZE ]; // define SIZE as per your need
char last[ SIZE ];
scanf("%s", first);
printf("%s", last); // FIXME : last is again not initialized
return 1;
}
动态版,
int main(void)
{
char *first = malloc( SIZE ); // define SIZE as per your need
char *last = malloc( SIZE );
scanf("%s", first);
printf("%s", last); // FIXME : last is again not initialized
free( first ); first = NULL;
free( last ); last = NULL;
return 1;
}