首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >是什么导致这段代码出现分段错误?

是什么导致这段代码出现分段错误?
EN

Stack Overflow用户
提问于 2011-12-08 01:01:00
回答 3查看 1.6K关注 0票数 3

在下面的代码中,我在一行看到了一个分段错误,它是从signaling_thread_->Send(this, id, data);类的析构函数调用的。

代码语言:javascript
复制
bool PeerConnectionProxy::Send(uint32 id, talk_base::MessageData* data) {
  if (!signaling_thread_)
    return false;
  signaling_thread_->Send(this, id, data);
  return true;
}

在gdb中运行时,只要对该行执行(gdb) step操作,就会得到分段错误和堆栈跟踪:

代码语言:javascript
复制
Program received signal SIGSEGV, Segmentation fault.

0x00000000 in ?? ()
(gdb) bt
#0  0x00000000 in ?? ()
#1  0xa782eed4 in webrtc::PeerConnectionProxy::Send (this=0xab889e80, id=6, data=0xbfffc1e8)
    at third_party/libjingle/source/talk/app/webrtc/peerconnectionproxy.cc:219
#2  0xa782e91a in ~PeerConnectionProxy (this=0xab889e80, __in_chrg=<value optimised out>)
    at third_party/libjingle/source/talk/app/webrtc/peerconnectionproxy.cc:145

...

在这一行之前,我检查signaling_thread_是否是非空的,这和数据一样。我只是很困惑,究竟是什么导致了段错误,还是使堆栈以0x00000000结尾。代码仅在代码路径上通过析构函数进行分段错误。Send函数是从许多其他地方调用的,没有问题。

更新2011-12-08:

踏过第一步,打开拆卸程序,我明白了:

代码语言:javascript
复制
0xa772eed2  219   signaling_thread_->Send(this, id, data);
   0xa772eea4 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+24>:     8b 45 08   mov    0x8(%ebp),%eax
   0xa772eea7 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+27>:     8b 40 0c   mov    0xc(%eax),%eax
   0xa772eeaa <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+30>:     8b 00  mov    (%eax),%eax
   0xa772eeac <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+32>:     83 c0 40   add    $0x40,%eax
   0xa772eeaf <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+35>:     8b 08  mov    (%eax),%ecx
   0xa772eeb1 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+37>:     8b 45 08   mov    0x8(%ebp),%eax
   0xa772eeb4 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+40>:     8d 70 04   lea    0x4(%eax),%esi
   0xa772eeb7 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+43>:     8b 45 08   mov    0x8(%ebp),%eax
   0xa772eeba <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+46>:     8b 40 0c   mov    0xc(%eax),%eax
   0xa772eebd <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+49>:     8b 55 10   mov    0x10(%ebp),%edx
   0xa772eec0 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+52>:     89 54 24 0c    mov    %edx,0xc(%esp)
   0xa772eec4 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+56>:     8b 55 0c   mov    0xc(%ebp),%edx
   0xa772eec7 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+59>:     89 54 24 08    mov    %edx,0x8(%esp)
   0xa772eecb <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+63>:     89 74 24 04    mov    %esi,0x4(%esp)
   0xa772eecf <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+67>:     89 04 24   mov    %eax,(%esp)
=> 0xa772eed2 <_ZN6webrtc19PeerConnectionProxy4SendEjPN9talk_base11MessageDataE+70>:     ff d1  call   *%ecx

ecx是0x0,所以这就是段错误的原因,但我仍然不明白发生了什么。这行的其他代码看上去不会触及ecx,除非我只是读错了。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-12-08 22:21:20

最可能的原因是signaling_thread_是一个悬空的指针--它曾经指向某个东西,但是它是deleted,留下了一个不是空的指针,但是如果你试图对它做任何事情(比如调用它上的Send方法),它很可能会导致崩溃。

由于您说这是从析构函数调用的,所以删除调用很可能发生在同一个析构函数的前面。

票数 3
EN

Stack Overflow用户

发布于 2011-12-08 22:06:01

每当我有涉及析构函数的分段错误时,通常通过使析构函数virtual来解决。如果这对你来说还没有意义的话,别担心。

当要销毁一个对象时,首先调用析构函数,然后尝试释放该对象。通常,析构函数本身是完全成功的,但是试图释放内存的尝试由于一个模糊的问题而失败,我稍后将尝试描述这个问题。(此answer引用了C++标准的相关部分。)

您确定段错误发生在析构函数期间吗?也可能是在析构函数完成后立即发生的。请在相关的破坏者的末尾放一张打印件好吗?

我将假设析构函数本身是成功的,并且在试图释放内存期间,在析构函数之后立即发生错误。

考虑这一结构:

代码语言:javascript
复制
struct A {
    int x;
};
A a;

在这里,显然是&a == &(a.x)

B继承自A:

代码语言:javascript
复制
struct B : public A {
};
B b;

再说一遍,&b == &(b.x)

但如果涉及到虚拟方法,事情就会变得棘手。

代码语言:javascript
复制
struct C : public B {
   virtual void foo() {}
};
C c;

现在,&c != &(c.x)。这是因为c的第一个真正条目(依赖于编译器)实际上是一个vtable,它列出了foo()等函数的位置。现在设想下面的代码:

代码语言:javascript
复制
{
    A * p = new C;
    delete p;
}

delete p语句认为它处理的是A类型的对象,但实际上它处理的是C类型的对象。析构函数将正确操作,但是调用free(p)的尝试将是错误的,因为它没有使用正确的地址。就像int *p = malloc(100); free(p+1)

如果有疑问,请在每个类中放置一个virtual析构函数,如果您有可能继承它,在子类中使用虚拟函数。

票数 1
EN

Stack Overflow用户

发布于 2011-12-08 23:08:36

%ecx保存Send方法的虚拟表条目,该方法由于某种原因被归零。最常用的析构函数是在调用signaling_thread_之前删除PeerConnectionProxy::Send。另一种可能是在以前对signaling_thread_方法的调用中出现缓冲区溢出,该方法覆盖虚拟表条目。另一种可能是正在覆盖signaling_thread_指针的析构函数中出现缓冲区溢出。如果你把代码张贴到你的析构函数,我们可能可以缩小它。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8424866

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档