C++ 中“空引用”与“空指针”的区别

网络上有很多讨论C++ 的“引用”与“指针“的区别的文章,谈到区别,其中有一条:“引用不能为空(NULL),引用必须与合法的存储单元关联,指针则可以是NULL)”,但在实际应用中,有时候为了保持一致性,我们会抛开这个规则,人为创造出一个“空引用”。

很多情况下,“空引用”确实可以工作,以致于“引用不能为空”的忠告,被嘲笑为形式主义,仅仅是标准制定者的耸人听闻。一个“空引用”的例子是:

 
  1. int * a = NULL;

  2. int & b = *a;

于是当访问b的时候,程序异常出现了:

 
  1. void f(int & p)

  2. {

  3. p = 0;

  4. }

  5. f(b);

当然,可以增加点判断,修正这个问题:

 
  1. void f(int & p)

  2. {

  3. if (&p) p = 0;

  4. }

怎么样,是不是有点别扭?但是如果换成成指针,你要输入的字符数是一模一样的:

 
  1. void f(int * p)

  2. {

  3. if (p) *p = 0;

 于是,到底是使用“引用”还是“指针”,好像就是智者见智仁者见仁的事情了。

然而,然而。。。。。。

这真的一样吗?

我们来看看复杂一点的例子:

 
  1. // test.cpp

  2. #include <iostream>

  3. class A

  4. {

  5. int a;

  6. };

  7. class B

  8. {

  9. int b;

  10. };

  11. class C

  12. : public A, public B

  13. {

  14. int c;

  15. };

  16. void fb(B & b)

  17. {

  18. std::cout << &b << std::endl;

  19. }

  20. void fb(B * b)

  21. {

  22. std::cout << b << std::endl;

  23. }

  24. int main(int argc, char* argv[])

  25. {

  26. C * c = NULL;

  27. fb(c);

  28. fb(*c);

  29. return 0;

  30. }

编译运行一下看看:

$ ./test
0
0x4

咦,怎么&b不是0,也就是不是“空引用”了,这时候,即使加上判断,if (&b),也无济于事了。

大家也许注意到了,上面是linux环境运行,那么windows环境呢:

>test.exe
00000000
00000000

这时候,“空引用”保持了他的“空”属性,仅在windows平台做C++的开发者,可以松口气了。

这是怎么回事呢,是你的眼睛欺骗了你?也许是,但是CPU不会欺骗我们,从汇编代码可以看出本质。下面是linux平台编译的代码:

Dump of assembler code for function main:
   0x0804870a <+0>:     push   %ebp
   0x0804870b <+1>:     mov    %esp,%ebp
   0x0804870d <+3>:     and    $0xfffffff0,%esp
   0x08048710 <+6>:     sub    $0x20,%esp
   0x08048713 <+9>:     movl   $0x0,0x1c(%esp)
   0x0804871b <+17>:    cmpl   $0x0,0x1c(%esp)
   0x08048720 <+22>:    je     0x804872b <main+33>
   0x08048722 <+24>:    mov    0x1c(%esp),%eax
   0x08048726 <+28>:    add    $0x4,%eax
   0x08048729 <+31>:    jmp    0x8048730 <main+38>
   0x0804872b <+33>:    mov    $0x0,%eax
   0x08048730 <+38>:    mov    %eax,(%esp)
   0x08048733 <+41>:    call   0x80486df <fb(B*)>
   0x08048738 <+46>:    mov    0x1c(%esp),%eax
   0x0804873c <+50>:    add    $0x4,%eax
   0x0804873f <+53>:    mov    %eax,(%esp)
   0x08048742 <+56>:    call   0x80486b4 <fb(B&)>
   0x08048747 <+61>:    mov    $0x0,%eax
   0x0804874c <+66>:    leave  
   0x0804874d <+67>:    ret    

这是windows平台的:

wmain:
004114D0  push        ebp  
004114D1  mov         ebp,esp 
004114D3  sub         esp,0DCh 
004114D9  push        ebx  
004114DA  push        esi  
004114DB  push        edi  
004114DC  lea         edi,[ebp-0DCh] 
004114E2  mov         ecx,37h 
004114E7  mov         eax,0CCCCCCCCh 
004114EC  rep stos    dword ptr es:[edi] 
004114EE  mov         dword ptr [c],0 
004114F5  mov         eax,dword ptr [c] 
004114F8  mov         dword ptr [rc],eax 
004114FB  cmp         dword ptr [c],0 
004114FF  je          wmain+3Fh (41150Fh) 
00411501  mov         eax,dword ptr [c] 
00411504  add         eax,4 
00411507  mov         dword ptr [ebp-0DCh],eax 
0041150D  jmp         wmain+49h (411519h) 
0041150F  mov         dword ptr [ebp-0DCh],0 
00411519  mov         ecx,dword ptr [ebp-0DCh] 
0041151F  push        ecx  
00411520  call        fb (411118h) 
00411525  add         esp,4 
00411528  cmp         dword ptr [rc],0 
0041152C  je          wmain+6Ch (41153Ch) 
0041152E  mov         eax,dword ptr [rc] 
00411531  add         eax,4 
00411534  mov         dword ptr [ebp-0DCh],eax 
0041153A  jmp         wmain+76h (411546h) 
0041153C  mov         dword ptr [ebp-0DCh],0 
00411546  mov         ecx,dword ptr [ebp-0DCh] 
0041154C  push        ecx  
0041154D  call        fb (41108Ch) 
00411552  add         esp,4 
00411555  xor         eax,eax 
00411557  pop         edi  
00411558  pop         esi  
00411559  pop         ebx  
0041155A  add         esp,0DCh 
00411560  cmp         ebp,esp 
00411562  call        @ILT+345(__RTC_CheckEsp) (41115Eh) 
00411567  mov         esp,ebp 
00411569  pop         ebp  
0041156A  ret              

汇编代码有兴趣自己研究,不细说了。


回过头想想,两个平台的编译器的两种处理方式,都有他的合理性,windows平台增加了容错性,而linux平台在处理引用时减少判断,增加性能。这隐隐体现出windows与linux开发理念的不同。

最后,请大家记住,引用不能为空,如果可能存在空对象时,请使用指针。

(68条消息) C++ 中“空引用”与“空指针”的区别_luansxx的专栏-CSDN博客_c++ 引用为空

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值