一个平台上的c ++ segfault(MacOSX)而不是另一个平台(linux)

时间:2015-06-14 15:00:29

标签: c++ linux macos gcc llvm

我在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析构函数,segfault消失。

所以我仍然想知道为什么segfault不会出现在使用gcc 4.8.1的g ++的Linux上。 (另外,我想偏离主题,我想知道编程本身 - 在这段代码中处理索引擦除/内存删除的正确方法是什么?)

编辑2:

我认为这不是Vector.erase(Iterator) causes bad memory access的副本,因为我原来的问题与为什么我在一个平台而不是另一个平台上得到段错误有关。有可能另一个问题解释了segfault本身(不确定如何绕过这个问题......也许删除Word析构函数并从DeleteAll()调用erase而不是“delete”?但是析构函数对我来说虽然有意义.. 。),但如果它确实是代码中的错误,为什么gcc g ++没有选择它?

1 个答案:

答案 0 :(得分: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->second调用~Word从您正在迭代的地图中删除。这会使您的活动迭代器无效,从而导致未定义的行为。因为它是UB,它在一个平台而不是另一个平台上工作的事实基本上只是运气(或缺乏运气)。

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