一个未初始化的指针是如何在cin中创建SegFault的

How did an uninitialised pointer create a SegFault in cin?

本文关键字:cin SegFault 创建 指针 一个 初始化      更新时间:2023-10-16

我声明了一个指针,但没有初始化它。但正因为如此,我在cin语句中得到了一个与指针无关的segfault。这是代码:

char tempBuff[20];
char ** stat;
cout<<"All fine till here...";
cin>>tempBuff;
cout<<"Gotcha!";

这是输出,其中12是输入的数字:

All fine till here...12
Segmentation fault (core dumped)

如果指针stat被初始化为NULL,它工作得很好。如果我知道为什么会发生这种事,这将帮助我不再犯同样的错误。

谢谢!

更新:由于没有人能够用我给出的代码片段重现错误,以下是整个代码。我在GCC版本4.8.4上编译了代码以获得错误。

您被stdout的缓冲误导了。如果添加显式刷新:

cout << "Gotcha!" << std::flush;

您将得到以下输出:

/选择在这里之前一切都很好。。。12明白了!分段故障

使用valgrind运行会产生以下错误:

==5817==条件跳转或移动取决于未初始化的值===5817===在0x569E68B:____ strtol_l_internal(strtol_l.c:489)===5817===通过0x400BAF:addStudent()(选择.cpp:21)===5817===通过0x400C53:main(选择.cpp:30)===5817======5817===使用大小为8的未初始化值===5817===在0x569E68D:____ strtol_l_internal(strtol_l.c:490)===5817===通过0x400BAF:addStudent()(选择.cpp:21)===5817===通过0x400C53:main(选择.cpp:30)===5817======5817======5817===进程终止,信号11默认动作(SIGSEGV)===5817===地址0x400A29处映射区域的权限不正确===5817===在0x569E68D:____ strtol_l_internal(strtol_l.c:490)===5817===通过0x400BAF:addStudent()(选择.cpp:21)===5817===通过0x400C53:main(选择.cpp:30)

以下指令的哪一点:

newStud.mark = strtol(tempBuff, stat, 10);

事实上,您提供了指向strtol的未初始化指针,然后NULL对其进行测试(因此"条件跳转或移动取决于未初始化的值")。

然后,看到它不是(在您的情况下,它包含任意但非零的垃圾),继续将其结束指针存储在这个位置,从而触发segfault。

是否在代码的其他地方使用指针?根据我的经验,分段故障不一定发生在你期望的地方。它们甚至不总是发生在真正有错误的时候。例如,如果未初始化的指针恰好指向您允许的地址空间中的某个位置,则不会得到segfault。当您用完数组的末尾时,这种情况经常发生。

这里可能发生的情况是,当您声明stat时,您正在更改堆栈的外观,这也是tempBuff驻留在内存中的位置。在一种情况下,这可能会导致segfault,而在另一种情况中则不会导致错误,这很难说,因为你并不真正知道编译器在对你的代码做什么。如果你在linux平台上,我会尝试通过一个名为valgrind的程序来运行它,该程序会检查你的内存。

cout缓冲-缓冲区满时写入输出;当读取CCD_ 8时;或者当使用flush()或通过写入std::endl显式刷新cout时。

在您的情况下,这会导致输出具有欺骗性,因为您的程序在cout<<"Gotcha!";之后但在刷新缓冲区之前崩溃。

崩溃的原因是您从未初始化过stat,并且像您的代码和strol一样,取消对它的引用是未定义的。

然而,即使您将代码初始化为NULL,您的代码也是未定义的,因为取消引用空指针也是未定义(我不知道为什么您的代码在这种情况下不会崩溃,但这就是未定义行为的本质。编译器可能已经删除了测试,因为它知道这是一个无效的取消引用,因此不可能在有效的程序中发生。)。

如果您对strtol中的"结束指针"感兴趣,您应该向它传递一个指向变量的指针:

    char * stat = NULL;
    cout<<"All fine till here...";
    cin>>tempBuff;
    cout<<"Gotcha!";
    student newStud;
    newStud.mark = strtol(tempBuff, &stat, 10);
    if(stat)
            newStud.status == tempBuff[0];
    else
            newStud.status == 'P';

我不确定您对最终测试的意图是什么,因为strtol总是在"end"参数中返回一个有效的指针
(如果转换成功,它是转换值末尾的一个,否则是第一个参数的值。)

这样做的寓意是:在跟踪调试时,始终编写一个endl(或者使用cerr而不是cout)。