【问题标题】:Segmentation fault while iterating a std::set<std::string> in C++在 C++ 中迭代 std::set<std::string> 时出现分段错误
【发布时间】:2011-11-29 07:55:24
【问题描述】:

我的这部分代码(用于this project)给了我一个分段错误。源代码在here.

void PackageManager::install_package(string pname)
{
  if(repository->exists_package(pname)) {
    Package *pkg;
    ConcretePackage *cpkg;
    MetaPackage *mpkg;
    if(repository->is_virtual(pname)) {
      //code for dealing with meta packages
      mpkg = new MetaPackage(pname);
      pkg = mpkg;
      system->operator+(pname);
    } else {
      //code for dealing with concrete packages
      cpkg = new ConcretePackage(pname);
      pkg = cpkg;
      system->operator+(pname);
      if( cpkg->getDependencies().size() > 0) {
        for(set<string>::iterator sit = pkg->getDependencies().begin();
            sit!=pkg->getDependencies().end(); ++sit) {
          cout<<*sit<<endl;
          system->operator+(*sit);
        }
      }
    }
  } else {
    cout<<"Invalid Package Name"<<endl;
  }
}

这是我运行 gdb 和回溯时的错误。

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b6db03 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ()
   from /usr/lib/libstdc++.so.6
(gdb) backtrace
#0  0x00007ffff7b6db03 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ()
   from /usr/lib/libstdc++.so.6
#1  0x00000000004052e8 in PackageManager::install_package (this=0x7fffffffe280, pname=...) at packagemanager.cpp:39
#2  0x000000000040575a in main () at packagemanager.cpp:79

我正在尝试遍历一个集合并执行一些操作。如果需要,我可以编写更多代码。 如果有人可以将我引导到一个我可以学习理解这些段错误的地方,我也会很高兴。我对它们了解不多,当我遇到这些时,我往往会感到恐慌。

这是 System 类的 operator+。

void System::operator+(string pname)
{
  installed_packages.insert(pname);
  log.push_back("Added " + pname);
}

我知道设计不是最好的,但我正在尝试为这个项目实施清单中的项目,它涵盖了面向对象编程的各个领域。该清单也可以在 github 上找到。

我尝试通过调试器运行代码,打印出 *sit。它工作了一段时间然后崩溃。我对gdb了解不多。

【问题讨论】:

  • 什么是system-&gt;operator+()
  • @GregHewgill 从我在堆栈跟踪中看到的内容来看,错误来自operator&lt;&lt;of std::ostream 中的PackageManager::install_package。好像在for循环中。
  • @GregHewgill :我已将链接添加到我在 github 上的仓库。
  • @Drahakar :我添加了重载运算符的定义。另外,您如何从堆栈跟踪中推断出事情。我也希望能够做到这一点。

标签: c++ iterator segmentation-fault set


【解决方案1】:

StackOverflow 有几个“什么是分段错误?”风格问答:

What is a segmentation fault?

理想情况下,您正在使用调试器并能够逐行执行代码或放置断点的环境中工作。这可以帮助您隔离崩溃周围的情况。你已经在堆栈跟踪中有一个行号——我们假设它指向一个确凿的证据:

cout<<*sit<<endl;

但是使用调试器单步执行可以回答这样的问题,例如这是否在第一次通过循环时发生......如果不是,那么在哪个元素上发生。


更新:查看您在 GitHub 上的这段代码(不包括上述代码),我看到 ConcretePackage::getDependencies() 正在返回一个集合按值 而不是引用。这意味着每次您呼叫该成员时,您都会获得该集合的新副本。来自不同容器的迭代器不应该相互比较,即使它们是相同的类型:

comparing iterators from different containers

要解决这个问题,您可以更改:

for(set<string>::iterator sit = pkg->getDependencies().begin();
        sit!=pkg->getDependencies().end(); ++sit) { ... }

...进入:

set<string> deps = pkg->getDependencies();
for(set<string>::iterator sit = deps.begin(); sit!=deps.end(); ++sit) { ... }

...或者您可以更改 getDependencies 的定义以返回引用:

set<string>& ConcretePackage::getDependencies() {
    return dependencies;
}

研究以一种方式与另一种方式做这件事的原因留给学生作为练习。 :P


还有一些注意事项:

  • 对于要使用迭代器的集合类,您不需要针对零大小进行特殊测试。如果一个集合不包含任何元素,那么该集合的.begin() 将返回一个等于.end() 的迭代器。您上面的循环可以很好地处理这种情况,并且会立即退出。

  • 在代码中显式调用operator+ 而不对返回值做任何事情表明您可能有某种副作用。很少有人期望像a = b + c 这样的表达式会改变bc...而像b-&gt;operator+(c); 这样的一行代码表明你正在做类似的事情。虽然在技术上是可行的,但我会避免它。请参阅此处的第 2 点:Operator overloading

  • 当您发布代码示例时,请尽量保持它们的可读性,并且不需要显示大量滚动条。如果您在预览中注意到它放置了一个长的水平滚动条,请断开线条。不要为每个大括号使用单独的行,而是将它们放在与条件相同的行上。 (无论您在代码库中使用什么约定,在线寻求技术帮助时越简短越好。)

(也提供上下文。如果你不说这是家庭作业和你自己的设计,那么像我这样的人会去谷歌搜索你正在使用什么样的包管理器。幸运的是我找到了你的程序员.stackexchange.com 发布...)

【讨论】:

  • 你说得对,我应该提供上下文。我很简短,我不想发布太多代码。我已经把这部分归零,并认为它是最相关的。我最初没有测试 0 但在收到该错误后我认为它可能会以某种方式阻止它。如果您能通过查看代码提供一些建议,我将不胜感激。
  • @nikhil 我找到了你的问题(好吧,无论如何,这个问题的原因)。更新。另外我注意到你的 Makefile 是错误的,可能会让你很沮丧,因为它不会在编辑后重建正确的文件。如果您要使用 make ,请阅读它以确保您正确使用它:cs.umd.edu/class/fall2002/cmsc214/Tutorial/makefile.html
  • 顺便说一句,您应该将+ 的运算符重载的使用作为调用日志函数的荒谬方式。如果您必须在某处演示运算符重载,我相信您可以做一些更合理的事情,其中​​运算符重载允许您从包中“添加”和“减去”依赖项(例如)......
  • 您先生,是救生员。现在,如果您可以指导我学习一些教程,我可以在其中学习自己调试分段错误。我尝试了一些 gdb 教程,但它们处理了明显的错误,并且在我实际编码时似乎没有任何应用。我确实查看了你的博客,它真的很好。感谢您抽出时间帮助像我这样的初学者。
  • @nikhil 很高兴能提供帮助,参与这样的事情很有趣,我在学习编程时并不存在......现在我可以告诉人们我在印度挽救了一个孩子的生命。 :) 感谢您查看博客,实际上我一直在做 StackOverflow 而不是为它写作。至于调试,您需要一个 IDE,我认为这方面的建议是一个完全独立的问题!询问它并概述您拥有哪些工具和平台,以及对您的课程有哪些限制……并详细说明您目前遇到的让您陷入困境的困惑。
猜你喜欢
  • 2014-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-28
  • 1970-01-01
相关资源
最近更新 更多