为什么这会导致段。错误,如何使用 GDB 进行调试?
Why would this cause a Seg. fault, and how can I use GDB to debug it?
代码本身非常简单。我正在使用 Catch2 进行单元测试(我真的很喜欢它的界面)并进入 gdb
,但没有获得任何有用的 Seg 信息。上述简单代码抛出的错误。
我很清楚是什么导致了这个问题,但我不知道为什么,或者我是如何得到来自 gdb
的违规代码行(我广泛使用 Python 等价物,pdb
,但 Python 中的错误似乎要简单得多)。
Flop.hpp
#ifndef FLOP
#define FLOP
class Flop {
private:
int tiles_[200][200][200];
public:
Flop();
}
#endif
Flop.cpp
#include "Flop.hpp"
Flop::Flop() { }
test_Flop.cpp
#include "catch.hpp"
#include "Flop.hpp"
SCENARIO("I bang my head against a wall") {
Flop flop;
WHEN("I try to run this test") {
THEN("This program SEGFAULTs") {
REQUIRE(1==1);
}
}
}
main.cpp 包含它应有的一切,以及下载的 catch.hpp(按照教程的说明).
我用 g++ Flop.cpp test_Flop.cpp main.cpp -o run_test
和 运行 用 gdb -ex run --args ./run_test -b
编译它,这允许 Catch2 进入调试器。结果是这样的:
Program received signal SIGSEGV, Segmentation fault.
0x0000555555566e9e in ____C_A_T_C_H____T_E_S_T____0() ()
有回溯:
#0 0x0000555555566e9e in ____C_A_T_C_H____T_E_S_T____0() ()
#1 0x000055555557e15e in Catch::TestInvokerAsFunction::invoke() const ()
#2 0x000055555557d7b1 in Catch::TestCase::invoke() const ()
#3 0x0000555555577f0a in Catch::RunContext::invokeActiveTestCase() ()
#4 0x0000555555577c59 in Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) ()
#5 0x000055555557671b in Catch::RunContext::runTest(Catch::TestCase const&) ()
#6 0x00005555555797cc in Catch::(anonymous namespace)::TestGroup::execute() ()
#7 0x000055555557ab49 in Catch::Session::runInternal() ()
#8 0x000055555557a853 in Catch::Session::run() ()
#9 0x00005555555b6195 in int Catch::Session::run<char>(int, char const* const*) ()
#10 0x000055555558fdf0 in main ()
好的。因此,SIGSEGV
表示我们试图 read/write 到进程无法访问的内存。如果我在 Flop.hpp 中说 int tiles_[10][10][10]
,那么一切正常。那么将 tiles_
设置为更大的大小是在某种程度上预留了一块无法访问的内存吗?我是 C++ 的新手(因此在我编写程序时对实际思考计算机正在发生的事情是新的)所以如果我错了请纠正我,但是 int tiles_[200][200][200]
不应该占用超过 32MB 的内存,对吧?
因此,我有几个问题:
- 为什么这会导致分段错误?
- 如何使用
gdb
将我带到有问题的代码行?这段代码的未简化版本总共有几百行。幸运的是,我的问题很早就出现在 class 的定义中,但是注释掉所有内容并(煞费苦心地)逐行取消注释仍然需要一段时间,这就是 gdb
的目的是为了防止!
数组的大小
int tiles_[200][200][200];
大约是 30 MB,假设 sizeof(int) == 4
。
这大于典型的堆栈大小限制,因此您将在堆栈外写入 space,当您使用
创建此类自动变量时,您可以使用该堆栈
Flop flop;
程序可用的堆栈量通常限制在几 MB 左右,具体取决于 OS 和设置。
gdb
给出了段错误的位置:测试函数的入口。函数中局部变量的堆栈 space 通常在进入函数时分配,因此这是堆栈溢出可能出现在分段错误中的地方(或者取决于堆栈的处理方式,当输出 -越界堆栈 space 是 written/read 第一次)。
不要将大型对象直接保存为成员或使用自动存储持续时间(即在堆栈上)。而是通过指针间接动态分配大对象(即在自由存储/堆上)。
最简单的方法是使用 std::vector
而不是内置数组。这通常也比内置数组更可取,如果您需要存储多个相同类型的对象,这应该是您的默认选择。
在需要编译时大小的非分配数组的特定情况下,std::array
也优于内置数组。这样你就可以完全避免使用内置数组。
另外,std::unique_ptr<...>
允许您在动态分配的间接寻址中包装任何对象类型(也包括内置数组)。
代码本身非常简单。我正在使用 Catch2 进行单元测试(我真的很喜欢它的界面)并进入 gdb
,但没有获得任何有用的 Seg 信息。上述简单代码抛出的错误。
我很清楚是什么导致了这个问题,但我不知道为什么,或者我是如何得到来自 gdb
的违规代码行(我广泛使用 Python 等价物,pdb
,但 Python 中的错误似乎要简单得多)。
Flop.hpp
#ifndef FLOP
#define FLOP
class Flop {
private:
int tiles_[200][200][200];
public:
Flop();
}
#endif
Flop.cpp
#include "Flop.hpp"
Flop::Flop() { }
test_Flop.cpp
#include "catch.hpp"
#include "Flop.hpp"
SCENARIO("I bang my head against a wall") {
Flop flop;
WHEN("I try to run this test") {
THEN("This program SEGFAULTs") {
REQUIRE(1==1);
}
}
}
main.cpp 包含它应有的一切,以及下载的 catch.hpp(按照教程的说明).
我用 g++ Flop.cpp test_Flop.cpp main.cpp -o run_test
和 运行 用 gdb -ex run --args ./run_test -b
编译它,这允许 Catch2 进入调试器。结果是这样的:
Program received signal SIGSEGV, Segmentation fault.
0x0000555555566e9e in ____C_A_T_C_H____T_E_S_T____0() ()
有回溯:
#0 0x0000555555566e9e in ____C_A_T_C_H____T_E_S_T____0() ()
#1 0x000055555557e15e in Catch::TestInvokerAsFunction::invoke() const ()
#2 0x000055555557d7b1 in Catch::TestCase::invoke() const ()
#3 0x0000555555577f0a in Catch::RunContext::invokeActiveTestCase() ()
#4 0x0000555555577c59 in Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) ()
#5 0x000055555557671b in Catch::RunContext::runTest(Catch::TestCase const&) ()
#6 0x00005555555797cc in Catch::(anonymous namespace)::TestGroup::execute() ()
#7 0x000055555557ab49 in Catch::Session::runInternal() ()
#8 0x000055555557a853 in Catch::Session::run() ()
#9 0x00005555555b6195 in int Catch::Session::run<char>(int, char const* const*) ()
#10 0x000055555558fdf0 in main ()
好的。因此,SIGSEGV
表示我们试图 read/write 到进程无法访问的内存。如果我在 Flop.hpp 中说 int tiles_[10][10][10]
,那么一切正常。那么将 tiles_
设置为更大的大小是在某种程度上预留了一块无法访问的内存吗?我是 C++ 的新手(因此在我编写程序时对实际思考计算机正在发生的事情是新的)所以如果我错了请纠正我,但是 int tiles_[200][200][200]
不应该占用超过 32MB 的内存,对吧?
因此,我有几个问题:
- 为什么这会导致分段错误?
- 如何使用
gdb
将我带到有问题的代码行?这段代码的未简化版本总共有几百行。幸运的是,我的问题很早就出现在 class 的定义中,但是注释掉所有内容并(煞费苦心地)逐行取消注释仍然需要一段时间,这就是gdb
的目的是为了防止!
数组的大小
int tiles_[200][200][200];
大约是 30 MB,假设 sizeof(int) == 4
。
这大于典型的堆栈大小限制,因此您将在堆栈外写入 space,当您使用
创建此类自动变量时,您可以使用该堆栈Flop flop;
程序可用的堆栈量通常限制在几 MB 左右,具体取决于 OS 和设置。
gdb
给出了段错误的位置:测试函数的入口。函数中局部变量的堆栈 space 通常在进入函数时分配,因此这是堆栈溢出可能出现在分段错误中的地方(或者取决于堆栈的处理方式,当输出 -越界堆栈 space 是 written/read 第一次)。
不要将大型对象直接保存为成员或使用自动存储持续时间(即在堆栈上)。而是通过指针间接动态分配大对象(即在自由存储/堆上)。
最简单的方法是使用 std::vector
而不是内置数组。这通常也比内置数组更可取,如果您需要存储多个相同类型的对象,这应该是您的默认选择。
在需要编译时大小的非分配数组的特定情况下,std::array
也优于内置数组。这样你就可以完全避免使用内置数组。
另外,std::unique_ptr<...>
允许您在动态分配的间接寻址中包装任何对象类型(也包括内置数组)。