为什么我会随机出现段错误?

时间:2009-05-21 17:46:33

标签: c++ stl segmentation-fault

这对我来说很奇怪,但是当我启动程序时,我遇到了意想不到的随机分段错误。有时它会起作用,有时会崩溃.. 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()的替代;但同样的问题发生了!

编辑:

我想我明白了...一个线程正在试图利用vehicles_distances_而它被清除..(?)

编辑:

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

谢谢大家!

13 个答案:

答案 0 :(得分:10)

首先,对所有人的爱,嗯,可爱,不要使用Dev-C ++ 。我希望我知道人们如何继续遇到那块垃圾。它没有被维护,即使它被维护,它仍然是一个缺乏非常基本功能的垃圾。抛弃它,寻找无数更好的免费替代品之一。

现在,关于你的问题:你的程序会随机发生段错误,因为你之前已经做过非法行为。不要那样做。 ;)

如果你的程序在某处写出了界限,可能会发生任何事情。它可能会打到一个未分配的页面,在这种情况下,您会遇到段错误。或者它可能会在分配给您的进程的页面上查找未使用的数据,在这种情况下它将没有任何实际效果(除非之后正确初始化,覆盖您的第一次,非法,写入,然后您尝试从中读取期望原始(无效)值仍然存在。或者它可能会触及实际使用的数据,在这种情况下,当程序尝试读取该数据时,您将在以后收到错误。

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

所以是的,这些错误很难找到。我可以给出的最好的建议是:1)在代码中撒上断言以确保维护基本不变量,以及2)逐步完成程序,并在每一步中验证您是否正在读取或写入不满足的地址属于你。

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

我相信GCC可以选择做类似的事情(但默认情况下没有启用)。

Segfaults很讨厌。一旦人们被这样的错误咬了几次,他们往往会因为避免访问内存而变得更加自律。 :)

答案 1 :(得分:6)

您可以尝试使用Valgrind作为帮助查找问题的方法。考虑到你在问题中提出的那一行,我不得不猜测你已经破坏了堆,或者你有堆栈问题。

答案 2 :(得分:6)

显而易见的问题是“什么是_p”。在调试器中,您应该能够查看callstack。按照_p回到原点。确认其大小正确,尚未删除,确实存在。

如果这并不容易,那么总会有蛮力的方法来评论随机(可疑)代码,直到它工作或返回并与已知的工作副本进行区分。

答案 3 :(得分:2)

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

答案 4 :(得分:2)

您可以在程序开头使用_CrtSetDbgFlag()来启用一些堆调试选项。另见The CRT Debug Heap。这可以帮助您追踪您在内存中做坏事的地方。它可以在Microsoft的C运行时中使用,默认情况下MinGW编译器会链接到该运行时。如果你改为使用GNU的C运行时,那么你将无法走这条路。

答案 5 :(得分:2)

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

  • 未初始化的变量(尤其是用于数组访问的索引)
  • 释放或删除后使用的内存。
  • 数组边界外的数组访问
  • 在未检查NULL的情况下使用的内存分配(如果使用new则不会出现问题,因为它不会返回NULL,会引发异常)

答案 6 :(得分:2)

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

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

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

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

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

这不排除静态初始化是问题的原因;即使它在main()之前没有崩溃,您仍然可能无法正确设置数据结构。但是,如果你从未接触到fprintf,那么你知道静态初始化是原因。

答案 7 :(得分:1)

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

然后检查核心文件

  

核心a.out.core

看看堆栈

  

BT

答案 8 :(得分:1)

这很可能与无效迭代器有关 - 搜索迭代容器和/或将迭代器保存到容器中并同时删除/插入元素的地方。
并且正如其他人所说的那样 - 使用一个调用 - 找到确切的位置。

答案 9 :(得分: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);
    }

答案 10 :(得分:1)

当您最终完成经过全面测试的代码时,您必须要做的第一件事就是调试堆栈,直到您最终使用自己的代码,在那里您将找到导致此问题的信息发生。

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

如果没有这些信息,我们无法帮助您。 ; - )

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

答案 11 :(得分:1)

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

答案 12 :(得分:-1)

调试器应该让你上调用栈。通过这种方式,您应该能够在自己的代码中看到导致seg错误的位置。