程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++ 中“空引用”與“空指針”的區別

C++ 中“空引用”與“空指針”的區別

編輯:C++入門知識

網絡上有很多討論C++ 的“引用”與“指針“的區別的文章,談到區別,其中有一條:“引用不能為空(NULL),引用必須與合法的存儲單元關聯,指針則可以是NULL)”,但在實際應用中,有時候為了保持一致性,我們會拋開這個規則,人為創造出一個“空引用”。

很多情況下,“空引用”確實可以工作,以致於“引用不能為空”的忠告,被嘲笑為形式主義,僅僅是標准制定者的聳人聽聞。一個“空引用”的例子是:

[cpp]
int * a = NULL; 
int & b = *a; 

int * a = NULL;
int & b = *a;於是當訪問b的時候,程序異常出現了:

[cpp]
void f(int & p) 

    p = 0; 

f(b); 

void f(int & p)
{
    p = 0;
}
f(b);當然,可以增加點判斷,修正這個問題:

[cpp]
void f(int & p) 

    if (&p) p = 0; 

void f(int & p)
{
    if (&p) p = 0;
}
怎麼樣,是不是有點別扭?但是如果換成成指針,你要輸入的字符數是一模一樣的:

[cpp]
void f(int * p) 

    if (p) *p = 0; 
}  

void f(int * p)
{
    if (p) *p = 0;
}  於是,到底是使用“引用”還是“指針”,好像就是智者見智仁者見仁的事情了。

 

然而,然而。。。。。。

 

這真的一樣嗎?

 

我們來看看復雜一點的例子:

[cpp]
// test.cpp  
 
#include <iostream>  
 
class A 

    int a; 
}; 
 
class B 

    int b; 
}; 
 
class C 
    : public A, public B 

    int c; 
}; 
 
void fb(B & b) 

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

 
void fb(B * b) 

    std::cout << b << std::endl; 

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

    C * c = NULL; 
 
    fb(c); 
 
    fb(*c); 
 
    return 0; 

// test.cpp

#include <iostream>

class A
{
    int a;
};

class B
{
    int b;
};

class C
    : public A, public B
{
    int c;
};

void fb(B & b)
{
    std::cout << &b << std::endl;
}

void fb(B * b)
{
    std::cout << b << std::endl;
}

int main(int argc, char* argv[])
{
    C * c = NULL;

    fb(c);

    fb(*c);

    return 0;
}
編譯運行一下看看:

[plain]
$ ./test 

0x4 

$ ./test
0
0x4咦,怎麼&b不是0,也就是不是“空引用”了,這時候,即使加上判斷,if (&b),也無濟於事了。

大家也許注意到了,上面是linux環境運行,那麼windows環境呢:

[plain]
>test.exe 
00000000 
00000000 

>test.exe
00000000
00000000這時候,“空引用”保持了他的“空”屬性,僅在windows平台做C++的開發者,可以松口氣了。

這是怎麼回事呢,是你的眼睛欺騙了你?也許是,但是CPU不會欺騙我們,從匯編代碼可以看出本質。下面是linux平台編譯的代碼:

[plain]
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     

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平台的:

[plain]
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               

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開發理念的不同。


最後,請大家記住,引用不能為空,如果可能存在空對象時,請使用指針。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved