3

这对我来说很奇怪,但是当我启动我的程序时,我遇到了一个意想不到的随机分段错误。有时它有效,有时它崩溃.. Dev-C++ 的调试器将我指向文件的一行:stl_construct.h

/**
   * @if maint
   * Constructs an object in existing memory by invoking an allocated
   * object's constructor with an initializer.
   * @endif
   */
  template<typename _T1, typename _T2>
    inline void
    _Construct(_T1* __p, const _T2& __value)
    {
      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // 402. wrong new expression in [some_]allocator::construct
     -> ::new(static_cast<void*>(__p)) _T1(__value);
    }

顺便说一句,我正在广泛使用 STL。我应该怎么做才能检测到段错误的起源?有什么工具可以提供帮助吗?可能导致这样的随机崩溃的原因是什么。

编辑:

我的程序大约有 5000 行代码。我不知道我必须显示什么代码才能获得帮助,因为我不知道问题的根源,我从调试器得到的只是它与 STL 有关。

编辑:

我搬到了Code::Blocks现在,这里是调用堆栈:

#0 00464635 std::_Construct<std::pair<double const, int>, std::pair<double const, int> >(__p=0xb543e8, __value=@0x10) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_construct.h:81)
#1 00462306 std::_Rb_tree<double, std::pair<double const, int>, std::_Select1st<std::pair<double const, int> >, std::less<double>, std::allocator<std::pair<double const, int> > >::_M_create_node(this=0x406fe50, __x=@0x10) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_tree.h:367)
#2 00461DA7 std::_Rb_tree<double, std::pair<double const, int>, std::_Select1st<std::pair<double const, int> >, std::less<double>, std::allocator<std::pair<double const, int> > >::_M_clone_node(this=0x406fe50, __x=0x0) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_tree.h:379)
#3 004625C6 std::_Rb_tree<double, std::pair<double const, int>, std::_Select1st<std::pair<double const, int> >, std::less<double>, std::allocator<std::pair<double const, int> > >::_M_copy(this=0x406fe50, __x=0x0, __p=0x406fe54) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_tree.h:1029)
#4 00462A9D std::_Rb_tree<double, std::pair<double const, int>, std::_Select1st<std::pair<double const, int> >, std::less<double>, std::allocator<std::pair<double const, int> > >::_Rb_tree(this=0x406fe50, __x=@0xb59a7c) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_tree.h:559)
#5 0045A928 std::map<double, int, std::less<double>, std::allocator<std::pair<double const, int> > >::map(this=0x406fe50, __x=@0xb59a7c) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_map.h:166)
#6 0040B7E2 VehicleManager::get_vehicles_distances(this=0xb59a50) (C:/Program Files/CodeBlocks/MinGW/projects/AHS/VehicleManager.cpp:232)
#7 00407BDA Supervisor::IsMergeInstruction(id_vehicle=1) (C:/Program Files/CodeBlocks/MinGW/projects/AHS/Supervisor.cpp:77)
#8 00408430 CheckingInstructionsThread(arg=0x476100) (C:/Program Files/CodeBlocks/MinGW/projects/AHS/Supervisor.cpp:264)
#9 00413950 _glfwNewThread@4() (??:??)
#10 75A24911    KERNEL32!AcquireSRWLockExclusive() (C:\Windows\system32\kernel32.dll:??)
#11 00476100    std::__ioinit() (??:??)
#12 0406FFD4    ??() (??:??)
#13 76E5E4B6    ntdll!RtlInitializeNtUserPfn() (C:\Windows\system32\ntdll.dll:??)
#14 00476100    std::__ioinit() (??:??)
#15 70266582    ??() (??:??)
#16 00000000    ??() (??:??)

更多精度:

1/ 这是一个多线程应用程序。2/ 方法:get_vehicles_distances(); 返回地图。3/ 地图可能在 IsMergeInstruction() 调用时尚未初始化;

编辑:

显然导致段错误的行是:

vehicles_distances_.erase(vehicles_distances_.begin(), vehicles_distances_.end());

Vehicles_distances_ 是地图。此行是方法的一部分: VehicleManager::MoveAllVehicles();

void VehicleManager::MoveAllVehicles() {

     vehicles_distances_.erase(vehicles_distances_.begin(), vehicles_distances_.end());

     vector<Vehicle>::iterator iter_end = VehicleManager::vehicles_.end();
     for(vector<Vehicle>::iterator iter = VehicleManager::vehicles_.begin();
     iter != iter_end; ++iter) {

          (*iter).MoveVehicle();

          vehicles_distances_[(*iter).get_vec_vehicle_position().y] = (*iter).get_id_vehicle();

     }

}

那有什么问题?

编辑:

我尝试使用 map::clear(); 作为 map::erase() 的替代品;但同样的问题发生了!

编辑:

我想我明白了...一个线程正试图在清除车辆时使用车辆距离..(?)

编辑:

问题解决了!所以它来自 map::erase(); 正如预期的那样。我通过创建另一个映射变量来绕过该问题,其中该对<key, value>被反转,因此我可以更新地图。(因为我需要的关键是距离,并且距离不是唯一的,因为它每次都在变化,但 id_vehicle 是唯一的!)。最后我只是拿了那张地图,<key, value>再次反转并将其转移到可以在每个循环中重新声明的原始地图......

感谢大家 !

4

13 回答 13

10

首先,为了所有可爱的事物,不要使用 Dev-C++。我希望我知道人们是如何不断撞上那块垃圾的。它已经很多年没有维护了,即使它被维护了,它仍然是一个缺乏基本功能的有缺陷的垃圾。放弃它,选择无数更好的免费替代品之一。

现在,关于您的问题:您的程序会随机出现段错误,因为您之前做了一些非法的事情。不要那样做。;)

如果您的程序在某处写入越界,则任何事情都可能发生。它可能会碰到一个未分配的页面,在这种情况下你会得到一个段错误。或者它可能会在分配给您的进程的页面上遇到未使用的数据,在这种情况下它不会产生任何实际效果(除非它在之后正确初始化,覆盖您的第一个非法写入,然后您尝试从中读取, 期望原始(无效)值仍然存在。或者它可能会命中实际正在使用的数据,在这种情况下,当程序尝试读取该数据时,您稍后会遇到错误。

读取数据时存在几乎相同的场景。您可能很幸运并立即得到一个段错误,或者您可以访问未使用和未初始化的内存,并读取垃圾数据(这很可能会在以后使用该数据时导致错误),或者您可以从内存地址读取已经在使用(这也会给你垃圾)。

所以是的,这些错误很难找到。我能给出的最好建议是 1)在你的代码中撒上断言,以确保维护基本的不变量,以及 2)逐步执行程序,并且在每一步,验证你没有读取或写入不'的地址' t 属于你。

MSVC 默认启用了一个安全 SCL 选项,它将对 STL 代码执行边界检查,这有助于发现此类错误。

我相信 GCC 可以选择执行类似的操作(但默认情况下未启用)。

段错误是讨厌的。一旦人们多次被这样的错误所困扰,他们往往会变得更加自律,避免越界访问内存。:)

于 2009-05-21T17:56:42.340 回答
6

您可以尝试使用Valgrind来帮助找到问题。鉴于您在问题中提出的行,我不得不猜测您已经损坏了堆,或者您有堆栈问题。

于 2009-05-21T17:50:39.073 回答
6

显而易见的问题是“_p 是什么”。在调试器中,您应该能够查看调用堆栈。跟随 _p 回到它的原点。确认它的大小正确,它没有被删除,并且它确实存在。

如果这并不容易,总是有一些蛮力的方法来注释掉随机(可疑的)代码,直到它工作或返回并与已知的工作副本进行比较。

于 2009-05-21T17:51:35.970 回答
2

这是非常模糊的,所以几乎不可能回答。一个明显的建议是检查您是否正在初始化所有变量。一些编译器会在调试中清零你未初始化的东西,而不是在发布时这样做,这会导致随机和意外的结果。

于 2009-05-21T17:51:11.430 回答
2

您可以_CrtSetDbgFlag()在程序的开头使用来启用一些堆调试选项。另请参见CRT 调试堆。这可以帮助您追踪您在哪里做坏事的记忆。它在 Microsoft 的 C 运行时中可用,MinGW 编译器默认链接到它。如果您改用 GNU 的 C 运行时,那么您将无法走这条路。

于 2009-05-21T17:58:28.513 回答
2

与 stl_construct.h 相比,问题更可能出在您的代码中。我假设该文件是 Dev-C++ 的 STL 发行版的一部分。分段错误可能发生在使用 stl_construct.h 中的模板实例化的代码中,但问题的根本原因将在其他地方。我会尝试通过在崩溃时获取堆栈跟踪来解决这个问题。对于堆栈跟踪中的每个函数(尤其是新编写的函数),尝试检查代码并查找以下类型的可能错误:

  • 未初始化的变量(尤其是用于数组访问的索引)
  • 释放或删除后使用的内存。
  • 数组边界外的数组访问
  • 未检查 NULL 就使用的内存分配(如果使用 new 则不是问题,因为它不返回 NULL,它会引发异常)
于 2009-05-21T17:59:22.707 回答
2

您的描述和调用堆栈表明程序在静态变量初始化期间崩溃。这是 C++ 的一个常见缺陷:没有简单的方法来控制静态变量的初始化顺序。

例如,您的程序可能有foo依赖于 object 的对象bar;但可能是程序在构造foo之前调用了构造函数bar。那会很糟糕,并且可能会导致您描述的那种问题。(是CheckingInstructionsThread产生线程的静态变量吗?这可能就是问题所在。)

要解决此问题,您可能需要查看程序的 .cpp 文件中的静态变量(包括类静态变量和全局变量),尤其是某些类类型的变量。修改构造函数以将一些跟踪写入 stderr 可能有帮助,也可能没有帮助;使用fprintf而不是cerr如果你这样做。

编辑:如果您不确定它是否与静态有关,请尝试在开头添加这样的一行main()

 fprintf(stderr, "main() entered\n");

这并不排除静态初始化是问题的原因;即使它之前没有崩溃main(),您仍然可能会错误地设置数据结构。但是,如果您从未使用过 fprintf,那么您就会知道静态初始化是原因所在。

于 2009-05-22T04:12:22.640 回答
1

当您最终获得经过彻底测试的代码而不是您的代码时,您必须做的第一件事就是向上调用堆栈,直到您最终获得自己的代码,在那里您将找到导致此问题发生的信息。

在调用堆栈中,最重要的位置是查看您的代码(调用者)和传递给您调用的函数(被调用者)的参数。

如果没有这些信息,我们将无法为您提供更多帮助。;-)

有关分段错误的更多信息:http
://en.wikipedia.org/wiki/Segmentation_fault (必读,另请参阅“另见”和“外部链接”中的链接)

于 2009-05-22T00:01:29.840 回答
1

使用核心文件运行调试器后,堆栈跟踪会告诉您什么?以正常方式运行 gdb gdb a.out

然后检查核心文件

核心 a.out.core

看看堆栈

BT

于 2009-05-21T17:53:32.813 回答
1

这很可能与无效的迭代器有关 - 搜索您迭代容器的位置和/或将迭代器保留在容器中并同时删除/插入元素。
正如其他人所指出的 - 使用调用堆栈来找到确切的位置。

于 2009-05-21T17:57:07.783 回答
1

它看起来像一个危险的功能:)

不过有一件事让我印象深刻。分配的内存去哪儿了?直观地说,我希望有一个指向指针的指针作为第一个参数,然后取消引用它。像这样:

template<typename _T1, typename _T2>
    inline void
    _Construct(_T1** __p, const _T2& __value)
    {

       ::new(static_cast<void*>(*__p)) _T1(__value);
    }

或者,对指针的引用:

template<typename _T1, typename _T2>
    inline void
    _Construct(_T1*& __p, const _T2& __value)
    {

       ::new(static_cast<void*>(__p)) _T1(__value);
    }
于 2009-05-21T23:39:32.210 回答
1

地图不是很友好的线程。如果您在线程代码中执行映射操作,您确实需要锁定对该映射的所有访问,并意识到您可能持有的任何迭代器都可能无效。

于 2009-05-22T03:51:13.240 回答
-1

调试器应该让您向上调用堆栈。这样,您应该能够在自己的代码中看到导致段错误的位置。

于 2009-05-21T17:51:32.427 回答