【问题标题】:Weird seg fault problem奇怪的段错误问题
【发布时间】:2010-06-05 20:38:31
【问题描述】:

您好,

我遇到了一个奇怪的段错误问题。我的应用程序在运行时转储了一个核心文件。挖掘后发现它死在了这个街区:

#include <lib1/c.h>  
...  
x::c obj;  
obj.func1();  

我在库 lib1 中定义了类 c:

namespace x  
{  
    struct c  
    {  
        c();  
        ~c();  
        void fun1();  
        vector<char *> _data;  
    };  
}  

x::c::c()  
{  
}  

x::c::~c()  
{  
    for ( int i = 0; i < _data.size(); ++i )  
        delete _data[i];  
}  

在我对 lib1.so 文件运行 nm 之前,我一直无法弄清楚:函数定义比我定义的要多:

x::c::c()  
x::c::c()  
x::c::~c()  
x::c::~c()  
x::c::func1()  
x::c::func2()  

在代码库中搜索后,我发现其他人在同一个命名空间中定义了一个同名的类,但在另一个库 lib2 中如下:

namespace x  
{  
    struct c  
    {  
       c();  
       ~c();  
       void func2();  
       vector<string> strs_;  
    };  
}  

x::c::c()
{
}

x::c::~c()
{
}

我的应用程序链接到依赖于 lib1 的 lib2。这种有趣的行为带来了几个问题:

  1. 为什么它会起作用?在链接 lib2(取决于 lib1)时,我预计会出现“多个定义”错误,但从未有过这样的错误。该应用程序似乎正在执行 func1 中定义的操作,只是它在运行时转储了一个核心。

  2. 附加调试器后,我发现我的应用程序调用了lib2中c类的ctor,然后调用了func1(在lib1中定义)。当超出范围时,它会调用 lib2 中 c 类的 dtor,其中会发生 seg 错误。谁能教我这怎么可能发生?

  3. 如何防止此类问题再次发生?有什么我可以使用的 C++ 语法吗?

忘了说我在 RHEL4 上使用的是 g++ 4.1,非常感谢!

【问题讨论】:

    标签: c++


    【解决方案1】:

    1.

    编译器不必诊断违反“单一定义规则”的情况。事实上,它们通常只有在您将多个目标文件链接在一起时才会知道。

    在链接时,有关原始类定义的信息可能不再存在(在编译器步骤之后不需要它们),因此通常不容易向用户标记一个类的多个定义。

    2.

    一旦你有了两个不同的定义,几乎任何事情都可能发生,你就处于未定义行为的领域。不管发生什么,都是可能的结果。

    3.

    最明智的做法是与团队的其他成员进行沟通。同意谁将使用哪些命名空间,你就不会遇到这些问题。否则,您需要在整个项目中使用文档工具或静态分析工具。许多此类工具将能够诊断多个不一致的类定义。

    【讨论】:

      【解决方案2】:

      只是猜测,但我没有看到任何 using namespace x;,所以它可能使用了一个命名空间而不是另一个命名空间?

      【讨论】:

        【解决方案3】:

        随着模板的出现,有必要允许对同名的代码体进行多个定义;编译器无法知道是否已经在另一个编译单元(即源文件)中生成了相同的模板代码。当链接器找到这些重复项时,它假定它们是相同的。您有责任确保它们是 - 这称为One Definition Rule

        【讨论】:

          【解决方案4】:

          在链接器级别,这是库插入。不幸的是,有效的符号绑定取决于链接器命令行上目标文件的顺序(这是,叹息,历史)。

          从您的描述看来,lib1 在链接器参数列表中排在第一位,lib2 排在第二位,插入来自lib1 的符号。这解释了从lib2 对构造函数和析构函数的调用,但从lib1 调用func1(因为lib2 中没有func1 派生符号,所以没有“隐藏”,调用绑定到lib1.)

          解决这个特定问题的方法是在链接器调用命令上反转库的顺序。

          【讨论】:

          • 这并不能真正解释缺少“重复定义”链接器错误。
          【解决方案5】:

          关于单一定义规则有很多答案。但是,对我来说,这看起来更像是缺少复制构造函数。

          详细说明:

          如果在您的对象上调用复制构造函数,那么您将遇到内存泄漏。这是因为 delete 将在同一组指针上调用两次。

          namespace x  
          {  
              struct c  
              {
                  c() {
                  }
          
                  ~c() {
                      for ( int i = 0; i < _data.size(); ++i )  
                          delete _data[i];
                  }
          
                  c(const c & rhs) {
                      for (int i=0; i< rhs.size(); ++i) {
                          int len = strlen(rhs[i]);
                          char *mem = malloc(len + 1); 
                          strncpy(mem, rhs[i], len + 1);
                          _data.push_back(mem);
                  }
          
                  void fun1();  
                  vector<char *> _data;  
              };  
          }  
          

          【讨论】:

          • 有趣,我从没想过copy ctor在这里会成为问题。你能详细说明一下吗?
          猜你喜欢
          • 2010-12-30
          • 1970-01-01
          • 2023-03-19
          • 2012-08-02
          • 2011-10-11
          • 1970-01-01
          • 1970-01-01
          • 2019-08-18
          • 1970-01-01
          相关资源
          最近更新 更多