c++ 段错误在一个平台 (MacOSX) 但不是另一个 (linux)
c++ segfault on one platform (MacOSX) but not another (linux)
我在 MacOSX 上遇到段错误("Segmentation fault: 11",在 gdb "Program received signal SIGSEGV, Segmentation fault" 中),出现在析构函数中,在该析构函数中容器通过迭代器循环并删除内存。我尝试过使用 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 中启动程序时,我也收到警告说 "warning: Could not open OSO archive file"。
在集群 linux 节点上,使用 gcc 4.8.1,我没有遇到段错误。任何想法可能是错误的以及如何避免 mac 上的段错误(最好使用 clang)?我真的不太了解编译器等。
编辑:
我想我发现了问题,但我仍然想了解为什么这在一个平台上有效,但在另一个平台上无效。这是一个最小的例子:
class字数:
#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 class:
#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 析构函数,段错误会消失。
所以我仍然想知道为什么段错误没有出现在 linux 上,使用 gcc 4.8.1 中的 g++。 (另外,我想跑题了,我想知道编程本身——在这段代码中处理索引 erasing/memory 删除的正确方法是什么?)
编辑 2:
我不认为这是 Vector.erase(Iterator) causes bad memory access 的重复,因为我最初的问题与为什么我在一个平台上出现段错误而不是另一个平台有关。另一个问题可能会解释段错误本身(不确定如何解决这个问题......也许删除 Word 析构函数并从 DeleteAll() 而不是 "delete" 调用擦除?但是那个析构函数对我来说很有意义...),但如果它确实是代码中的一个错误,为什么 gcc g++ 没有发现它?
这是一个问题:
~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
的副本并对其进行迭代,考虑在删除索引时不会改变索引的不同设计,或者使用 erase
returns 使循环安全的下一个有效迭代器(这意味着将 erase
提升到 DeleteAll
)。
我在 MacOSX 上遇到段错误("Segmentation fault: 11",在 gdb "Program received signal SIGSEGV, Segmentation fault" 中),出现在析构函数中,在该析构函数中容器通过迭代器循环并删除内存。我尝试过使用 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 中启动程序时,我也收到警告说 "warning: Could not open OSO archive file"。
在集群 linux 节点上,使用 gcc 4.8.1,我没有遇到段错误。任何想法可能是错误的以及如何避免 mac 上的段错误(最好使用 clang)?我真的不太了解编译器等。
编辑:
我想我发现了问题,但我仍然想了解为什么这在一个平台上有效,但在另一个平台上无效。这是一个最小的例子:
class字数:
#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 class:
#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 析构函数,段错误会消失。
所以我仍然想知道为什么段错误没有出现在 linux 上,使用 gcc 4.8.1 中的 g++。 (另外,我想跑题了,我想知道编程本身——在这段代码中处理索引 erasing/memory 删除的正确方法是什么?)
编辑 2:
我不认为这是 Vector.erase(Iterator) causes bad memory access 的重复,因为我最初的问题与为什么我在一个平台上出现段错误而不是另一个平台有关。另一个问题可能会解释段错误本身(不确定如何解决这个问题......也许删除 Word 析构函数并从 DeleteAll() 而不是 "delete" 调用擦除?但是那个析构函数对我来说很有意义...),但如果它确实是代码中的一个错误,为什么 gcc g++ 没有发现它?
这是一个问题:
~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
的副本并对其进行迭代,考虑在删除索引时不会改变索引的不同设计,或者使用 erase
returns 使循环安全的下一个有效迭代器(这意味着将 erase
提升到 DeleteAll
)。