C++抓住悬空参考

Any*_*orn 13 c++ debugging reference

假设下面的一段代码

struct S {
    S(int & value): value_(value) {}
    int & value_;
};

S function() {
    int value = 0;
    return S(value);   // implicitly returning reference to local value
}
Run Code Online (Sandbox Code Playgroud)

编译器不会产生警告(-Wall),这个错误很难捕获.

有哪些工具可以帮助解决这些问题

Nor*_*ame 11

有基于运行时的解决方案,用于检测代码以检查无效指针访问.到目前为止我只使用过mudflap(自4.0版以来就集成在GCC中).mudflap尝试跟踪代码中的每个指针(和引用),如果指针/引用实际指向其基类型的活动对象,则检查每次访问.这是一个例子:

#include <stdio.h>
struct S {
    S(int & value): value_(value) {}
    int & value_;
};

S function() {
    int value = 0;
    return S(value);   // implicitly returning reference to local value
}
int main()
{
    S x=function();
    printf("%s\n",x.value_); //<-oh noes!
}
Run Code Online (Sandbox Code Playgroud)

在启用mudflap的情况下编译它:

g++ -fmudflap s.cc -lmudflap
Run Code Online (Sandbox Code Playgroud)

并且运行给出:

$ ./a.out
*******
mudflap violation 1 (check/read): time=1279282951.939061 ptr=0x7fff141aeb8c size=4
pc=0x7f53f4047391 location=`s.cc:14:24 (main)'
      /opt/gcc-4.5.0/lib64/libmudflap.so.0(__mf_check+0x41) [0x7f53f4047391]
      ./a.out(main+0x7f) [0x400c06]
      /lib64/libc.so.6(__libc_start_main+0xfd) [0x7f53f358aa7d]
Nearby object 1: checked region begins 332B before and ends 329B before
mudflap object 0x703430: name=`argv[]'
bounds=[0x7fff141aecd8,0x7fff141aece7] size=16 area=static check=0r/0w liveness=0
alloc time=1279282951.939012 pc=0x7f53f4046791
Nearby object 2: checked region begins 348B before and ends 345B before
mudflap object 0x708530: name=`environ[]'
bounds=[0x7fff141aece8,0x7fff141af03f] size=856 area=static check=0r/0w liveness=0
alloc time=1279282951.939049 pc=0x7f53f4046791
Nearby object 3: checked region begins 0B into and ends 3B into
mudflap dead object 0x7089e0: name=`s.cc:8:9 (function) int value'
bounds=[0x7fff141aeb8c,0x7fff141aeb8f] size=4 area=stack check=0r/0w liveness=0
alloc time=1279282951.939053 pc=0x7f53f4046791
dealloc time=1279282951.939059 pc=0x7f53f4046346
number of nearby objects: 3
Segmentation fault
Run Code Online (Sandbox Code Playgroud)

需要考虑的几点:

  1. mudflap可以精确调整它应该检查和做什么.阅读http://gcc.gnu.org/wiki/Mudflap_Pointer_Debugging了解详情.
  2. 默认行为是在违规时引发SIGSEGV,这意味着您可以在调试器中找到违规.
  3. mudflap可能是一个婊子,特别是当您与未使用mudflap支持编译的库交互时.
  4. 它不会在创建悬空引用的地方吠叫(返回S(值)),仅在引用被取消引用时.如果您需要,那么您将需要一个静态分析工具.

PS要考虑的一件事是,在S()的复制构造函数中添加一个NON-PORTABLE检查,它断言value_没有绑定到寿命较短的整数(例如,如果*这位于"旧的"堆栈的插槽,它绑定的整数).这是高度机器特定的,当然可能很难得到,但只要它只用于调试就应该没问题.


Rod*_*ddy 5

我认为这不可能捕获所有这些,尽管某些编译器在某些情况下可能会发出警告。

还要记住,引用实际上是引擎盖下的指针,并且指针可能实现的许多射击自足场景仍然可行。

为了阐明我对“引擎盖下的指针”的含义,参加以下两节课。一个使用引用,另一个使用指针。

class Ref
{
  int &ref;
public:
  Ref(int &r) : ref(r) {};
  int get() { return ref; };
};

class Ptr
{
  int *ptr;
public:
  Ptr(int *p) : ptr(p) {};
  int get() { return *ptr; };
};
Run Code Online (Sandbox Code Playgroud)

现在,比较两者的生成代码。

@@Ref@$bctr$qri proc    near  // Ref::Ref(int &ref)
    push      ebp
    mov       ebp,esp
    mov       eax,dword ptr [ebp+8]
    mov       edx,dword ptr [ebp+12]
    mov       dword ptr [eax],edx
    pop       ebp
    ret 

@@Ptr@$bctr$qpi proc    near  // Ptr::Ptr(int *ptr)
    push      ebp
    mov       ebp,esp
    mov       eax,dword ptr [ebp+8]
    mov       edx,dword ptr [ebp+12]
    mov       dword ptr [eax],edx
    pop       ebp
    ret 

@@Ref@get$qv    proc    near // int Ref:get()
    push      ebp
    mov       ebp,esp
    mov       eax,dword ptr [ebp+8]
    mov       eax,dword ptr [eax]
    mov       eax,dword ptr [eax]
    pop       ebp
    ret 

@@Ptr@get$qv    proc    near // int Ptr::get()
    push      ebp
    mov       ebp,esp
    mov       eax,dword ptr [ebp+8]
    mov       eax,dword ptr [eax]
    mov       eax,dword ptr [eax]
    pop       ebp
    ret 
Run Code Online (Sandbox Code Playgroud)

指出不同?没有

  • @Steven-是时候该看一下引擎盖了!我已经更新了答案。 (4认同)