-1

我有简单的输入功能:

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;
}

任何想法 ?

4

4 回答 4

2
char *first;

scanf("%s", first);

first在调用之前未初始化scanf:调用之前的值是无效地址。

于 2013-06-24T17:36:04.717 回答
1

您要么希望char*在使用之前动态分配您scanf()的存储空间,要么可能切换到字符数组。

于 2013-06-24T17:40:36.077 回答
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值,因此扫描到它的段错误。


如果您有兴趣了解实际情况,可以查看由-S标志生成的程序集(或编译器的等效项),或者您可以printf("%p\n", first)然后查看您获得的地址并找出在那里映射的内容。

但实际上,为什么它不起作用并不重要。它不应该工作,唯一的解决方案是将指针正确初始化为有效的东西(正如 ouah 的回答和其他人解释的那样)。

于 2013-06-24T17:49:39.720 回答
0

您的first变量未初始化。要么将其声明为数组,要么根据您的需要动态分配。

  1. 数组版本,

    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;
    }
    
  2. 动态版,

    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;
    }
    
于 2013-06-24T17:41:18.813 回答