我的 C 程序以 segfault 结尾 - valgrind

让我们有一个基本的失败计划:

#include <iostream>

void fail() {
    int *p1;
    int *p2(NULL);
    int *p3 = p1;
    if (p3) {
        std::cout << *p3 << std::endl;
    } 
}

int main() { 
    fail();
}

构建它(添加 -g 以包含调试信息):

g++ -g -o main main.cpp

跑:

$ ./main
Segmentation fault (core dumped)
$

让我们用 valgrind 调试它:

$ valgrind ./main
==8515== Memcheck, a memory error detector
==8515== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8515== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==8515== Command: ./main
==8515==
==8515== Conditional jump or move depends on uninitialised value(s)
==8515==    at 0x400813: fail() (main.cpp:7)
==8515==    by 0x40083F: main (main.cpp:13)
==8515==
==8515== Invalid read of size 4
==8515==    at 0x400819: fail() (main.cpp:8)
==8515==    by 0x40083F: main (main.cpp:13)
==8515==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==8515==
==8515==
==8515== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==8515==  Access not within mapped region at address 0x0
==8515==    at 0x400819: fail() (main.cpp:8)
==8515==    by 0x40083F: main (main.cpp:13)
==8515==  If you believe this happened as a result of a stack
==8515==  overflow in your program's main thread (unlikely but
==8515==  possible), you can try to increase the size of the
==8515==  main thread stack using the --main-stacksize= flag.
==8515==  The main thread stack size used in this run was 8388608.
==8515==
==8515== HEAP SUMMARY:
==8515==     in use at exit: 72,704 bytes in 1 blocks
==8515==   total heap usage: 1 allocs, 0 frees, 72,704 bytes allocated
==8515==
==8515== LEAK SUMMARY:
==8515==    definitely lost: 0 bytes in 0 blocks
==8515==    indirectly lost: 0 bytes in 0 blocks
==8515==      possibly lost: 0 bytes in 0 blocks
==8515==    still reachable: 72,704 bytes in 1 blocks
==8515==         suppressed: 0 bytes in 0 blocks
==8515== Rerun with --leak-check=full to see details of leaked memory
==8515==
==8515== For counts of detected and suppressed errors, rerun with: -v
==8515== Use --track-origins=yes to see where uninitialised values come from
==8515== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
$

首先我们关注这个块:

==8515== Invalid read of size 4
==8515==    at 0x400819: fail() (main.cpp:8)
==8515==    by 0x40083F: main (main.cpp:13)
==8515==  Address 0x0 is not stack'd, malloc'd or (recently) free'd

第一行告诉我们,段错误是由读取 4 个字节引起的。第二行和第三行是调用堆栈。这意味着无效读取是在 main.cpp 的第 8 行的 fail() 函数上执行的,该函数由 main.cpp 的第 13 行调用。

看看 main.cpp 的第 8 行,我们看到了

std::cout << *p3 << std::endl;

但是我们先检查指针,那有什么不对?让我们检查另一个块:

==8515== Conditional jump or move depends on uninitialised value(s)
==8515==    at 0x400813: fail() (main.cpp:7)
==8515==    by 0x40083F: main (main.cpp:13)

它告诉我们第 7 行有一个单元化变量,我们读到它:

if (p3) {

这指向了我们检查 p3 而不是 p2 的行。但 p3 怎么可能未初始化?我们通过以下方式初始化:

int *p3 = p1;

Valgrind 建议我们重新运行 --track-origins=yes,让我们这样做:

valgrind --track-origins=yes ./main

valgrind 的论点就在 valgrind 之后。如果我们把它放在我们的程序之后,它将被传递给我们的程序。

输出几乎相同,只有一个区别:

==8517== Conditional jump or move depends on uninitialised value(s)
==8517==    at 0x400813: fail() (main.cpp:7)
==8517==    by 0x40083F: main (main.cpp:13)
==8517==  Uninitialised value was created by a stack allocation
==8517==    at 0x4007F6: fail() (main.cpp:3)

这告诉我们在第 7 行创建的未初始化值是在第 3 行创建的:

int *p1;

它指导我们未初始化的指针。