c++ segfault 在一个平台(MacOSX)但不是另一个(Linux)

Posted

技术标签:

【中文标题】c++ segfault 在一个平台(MacOSX)但不是另一个(Linux)【英文标题】:c++ segfault on one platform (MacOSX) but not another (linux) 【发布时间】:2015-06-14 15:00:29 【问题描述】:

我在 MacOSX 上遇到了一个段错误(“分段错误:11”,在 gdb“程序收到的信号 SIGSEGV,分段错误”中),出现在析构函数中,其中容器循环使用迭代器并删除了内存。我尝试过使用 clang++、g++(都是 LLVM 的一部分)和自制 g++。当迭代器第一次递增时出现段错误,并带有 gdb 消息(已用 clang++ 编译)

"0x000000010001196d in std::__1::__tree_node_base<void*>*     std::__1::__tree_next<std::__1::__tree_node_base<void*>*>(std::__1::__tree_node_base<void*>*) ()"

在 gdb 中启动程序时,我还会收到警告说“警告:无法打开 OSO 存档文件”。

在集群 linux 节点上,使用 gcc 4.8.1,我没有遇到段错误。任何想法可能有什么问题以及如何避免我的mac上的段错误(最好是clang)?我真的不太了解编译器等。


编辑:


我想我找到了问题所在,但是我仍然想了解为什么这在一个平台上有效,而在另一个平台上无效。这是一个最小的例子:

类词:

#ifndef WORD_H
#define WORD_H

#include <string> 
#include <map>

class Word 

    public:
        /*** Constructor ***/
        Word(std::string w) : m_word(w) 
            // Add word to index map, if it's not already in there
            std::map<std::string, Word*>::iterator it = index.find(w);
            if (it == index.end()) 
                index[w] = this;
            
        
        ~Word()  index.erase(m_word);  // Remove from index
        static void DeleteAll()    // Clear index, delete all allocated memory
            for (std::map<std::string, Word*>::const_iterator it = index.begin();
            it != index.end();
            ++it)
             delete it->second; 
        

    private:
        std::string m_word;
        static std::map<std::string, Word*> index; // Index holding all words initialized
;
#endif

WordHandler 类:

#ifndef _WORDHANDLER_H_
#define _WORDHANDLER_H_
#include <string>
#include "Word.h"
class WordHandler 
    WordHandler()      
    ~WordHandler()  Word::DeleteAll();    // clear memory

    void WordHandler::NewWord(const std::string word) 
         Word* w = new Word(word);
    
;
#endif

主程序:

#include <iostream>
#include "WordHandler.h"

int main () 

    std::cout << "Welcome to the WordHandler. " << std::endl;
    WordHandler wh;
    wh.NewWord("hallon");
    wh.NewWord("karl");

    std::cout << "About to exit WordHandler after having added two new words " << std::endl;
    return 0;

因此,当调用析构函数 ~WordHandler 时,程序退出时会发生段错误。我发现的原因是 Word 析构函数:Word 对象从地图中删除,这使得 DeleteAll() 函数很奇怪,因为地图在迭代时被更改(我想是某种双重删除)。通过完全删除 DeleteAll 或删除 Word 析构函数,段错误消失。

所以我仍然想知道为什么使用 gcc 4.8.1 的 g++ 在 linux 上没有出现段错误。 (另外,我猜题外话,我想知道编程本身——在这段代码中处理索引擦除/内存删除的正确方法是什么?)


编辑 2:


我不认为这是 Vector.erase(Iterator) causes bad memory access 的副本,因为我最初的问题与为什么我在一个平台上而不是另一个平台上出现段错误有关。另一个问题可能解释了段错误本身(不知道如何解决这个问题......也许删除 Word 析构函数并从 DeleteAll() 调用擦除而不是“删除”?但是那个析构函数对我来说很有意义...... .),但如果它确实是代码中的一个错误,为什么 gcc g++ 没有发现它?

【问题讨论】:

您可能拥有undefined behavior,但无法查看相关代码,很难说更具体的内容。请创建一个Minimal, Complete, and Verifiable Example 并向我们展示。 您是在删除迭代器指向的对象,然后尝试递增它吗? Vector.erase(Iterator) causes bad memory access 的可能副本 【参考方案1】:

这是个问题:

    ~Word()  index.erase(m_word);  // Remove from index
    static void DeleteAll()    // Clear index, delete all allocated memory
        for (std::map<std::string, Word*>::const_iterator it = index.begin();
        it != index.end();
        ++it)
         delete it->second; 
    

delete it-&gt;second 调用 ~Word,它会从地图中删除 您正在迭代的。这会使您的活动迭代器无效,从而导致未定义的行为。因为它是 UB,所以它可以在一个平台上运行但不能在另一个平台上运行这一事实基本上只是运气(或缺乏运气)。

要解决此问题,您可以制作index 的副本并对其进行迭代,考虑在删除索引时不会改变索引的不同设计,或者使用erase 返回下一个有效的事实使循环安全的迭代器(这意味着将erase 提升到DeleteAll)。

【讨论】:

以上是关于c++ segfault 在一个平台(MacOSX)但不是另一个(Linux)的主要内容,如果未能解决你的问题,请参考以下文章

使用向量和 fstream 的代码中的 Segfault 11? C++

返回包含向量的空向量时的C++ Segfault

试图添加到 C++ 中的 LinkedList。获取 SegFault

程序退出时的 Segfault (-11)

gcc+mingw 下的 C++ segfault 但不是 gcc+linux

C++:即使转换的对象不是 NULL,dynamic_cast 也会导致 SEGFAULT。怎么会这样?