小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

【轉(zhuǎn)】從底層匯編理解 c 引用實(shí)現(xiàn)機(jī)制 (轉(zhuǎn) 增加)

 techres 2012-05-13

從底層匯編理解 c++ 引用實(shí)現(xiàn)機(jī)制 (轉(zhuǎn)+增加)

  參考:http://blog.csdn.net/wanwenweifly4/article/details/6739687

紅色是我添加的,其他地方是原作者的。

主要是看了上面的這篇“從底層匯編理解 c++ 引用實(shí)現(xiàn)機(jī)制“的文章之后,覺(jué)得不錯(cuò)。就轉(zhuǎn)了過(guò)來(lái),同時(shí),對(duì)文中的程序都在自己的機(jī)器上驗(yàn)證了一下。

使用的G++版本:g++ (GCC) 4.5.1 20100924

如果要查看匯編后代碼與源碼的關(guān)系,我用的方法是:

先用g++生成帶有調(diào)試信息的目標(biāo)文件:g++ -g -c ref.cc

然后再利用objdump命令查看目標(biāo)文件ref.o:objdump -S ref.o

引用類(lèi)型到底是什么?它和指針有什么關(guān)系?它本身占用內(nèi)存空間嗎? 帶著這些疑問(wèn),我們來(lái)進(jìn)行分析。 先看代碼:

  1. #include <stdio.h>  

  2. #include <iostream>  

  3.   

  4. using namespace std;  

  5.   

  6.   

  7. void main()  

  8. {  

  9.    int x = 1;  

  10.    int &b = x;  

  11.  }   

int main()

{

int x=1;

int &b=x;

return 0;

}

  通過(guò)匯編查看代碼如下:



  1. 9:       int x = 1;  

  2. 00401048   mov         dword ptr [ebp-4],1  

  3. 10:      int &b = x;  

  4. 0040104F   lea         eax,[ebp-4]  

  5. 00401052   mov         dword ptr [ebp-8],eax  

00000000 <main>:

int main()

{

0: 55 push %ebp

1: 89 e5 mov %esp,%ebp

3: 83 ec 10 sub $0x10,%esp

int x=1;

6: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%ebp)

int &b=x;

d: 8d 45 f8 lea -0x8(%ebp),%eax

10: 89 45 fc mov %eax,-0x4(%ebp)

return 0;

13: b8 00 00 00 00 mov $0x0,%eax

}

18: c9 leave

19: c3 ret



  可以知道x的地址為ebp-4,b的地址為ebp-8,因?yàn)闂?nèi)的變量?jī)?nèi)存是從高往低進(jìn)行分配的。所以b的地址比x的低。
  lea eax,[ebp-4]  這條語(yǔ)句將x的地址ebp-4放入eax寄存器

  mov dword ptr [ebp-8],eax 這條語(yǔ)句將eax的值放入b的地址ebp-8中

  上面兩條匯編的作用即:將x的地址存入變量b中,這不和將某個(gè)變量的地址存入指針變量是一樣的嗎?

   所以從匯編層次來(lái)看,的確引用是通過(guò)指針來(lái)實(shí)現(xiàn)的。

   下面我們通過(guò)程序來(lái)驗(yàn)證,我們知道,在程序一層我們只要直接涉及到引用變量的操作,我們操作的總是被引用變量,即編譯器幫我們做了些手腳,總是在引用前面加上*。所以我們要讀取真正的“引用變量的值”,必須采取一定的策略,好吧,我們就按照變量在棧中分布的特點(diǎn)來(lái)繞過(guò)編譯器的這個(gè)特點(diǎn)。

[cpp] view plaincopyprint?

  1. #include <stdio.h>  

  2. #include <iostream>  

  3.   

  4. using namespace std;  

  5.   

  6. void main()  

  7. {  

  8.    int x = 1;  

  9.    int y = 2;  

  10.    int &b = x;  

  11.    printf("&x=%x,&y=%x,&b=%x,b=%x\n",&x,&y,&y-1,*(&y-1));  

  12.  }   

  輸出結(jié)果為:&x=12ff7c,&y=12ff78,&b=12ff74,b=12ff7c

#include <cstdio>

int main()

{

int x=1;

int y=2;

int &b=x;

printf("&x=%x,&y=%x,&b=%x,b=%x\n",&x,&y,&y-1,*(&y-1));

return 0;

}

輸出結(jié)果是:

&x=bfe1b308,&y=bfe1b304,&b=bfe1b300,b=8048460


這個(gè)地方的結(jié)果和作者不一樣,可以看后面的解釋。





  1. void main()  

  2. {  

  3.    int x = 1;  

  4.    int &b = x;  

  5.    printf("&x=%x,&b=%x\n",&x,&b);  

  6.  }   

  輸出結(jié)果為:&x=12ff7c,&b=12ff7c.

#include <cstdio>

int main()

{

int x=1;

int &b=x;

printf("&x=%x,&b=%x\n",&x,&b);

return 0;

}

輸出結(jié)果:&x=bfe74aa8,&b=bfe74aa8



  b的地址我們沒(méi)法通過(guò)&b獲得,因?yàn)榫幾g器會(huì)將&b解釋為:&(*b) =&x ,所以&b將得到&x。也驗(yàn)證了對(duì)所有的b的操作,和對(duì)x的操作等同。

  但是我們可以間接通過(guò)&y-1來(lái)得到b的地址,從而得到b的值:*(&y-1)  從結(jié)果可以知道,b的值即x的地址,從而可以知道,從地層實(shí)現(xiàn)來(lái)看,引用變量的確存放的是被引用對(duì)象的地址,只不過(guò),對(duì)于高級(jí)程序員來(lái)說(shuō)是透明的,編譯器 屏蔽了引用和指針的差別。

  下面是程序的變量在內(nèi)存棧中的分布,引用變量一樣也占用內(nèi)存空間,而且應(yīng)該是4個(gè)字節(jié)的空間。

   雖然從底層來(lái)說(shuō),引用的實(shí)質(zhì)是指針,但是從高層語(yǔ)言級(jí)別來(lái)看,我們不能說(shuō)引用就是指針,他們是兩個(gè)完全不同的概念。有人說(shuō)引用是受限的指針,這種說(shuō)法我不贊同,因?yàn)閺恼Z(yǔ)言級(jí)別上,指針和引用沒(méi)有關(guān)系,引用就是另一個(gè)變量的別名。對(duì)引用的任何操作等價(jià)于對(duì)被引用變量的操作。從語(yǔ)言級(jí)別上,我們就不要去考慮它的底層實(shí)現(xiàn)機(jī)制啦,因?yàn)檫@些對(duì)你是透明的。所以在面試的時(shí)候,如果面試的人問(wèn)到這個(gè)問(wèn)題,可以先從語(yǔ)言級(jí)別上談?wù)勔?,深入的?huà)就從底層的實(shí)現(xiàn)機(jī)制進(jìn)行分析。而不能什么條件沒(méi)有就說(shuō):引用就是指針,沒(méi)有差別,......之類(lèi)的回答


對(duì)于下面的程序:

#include <cstdio>

int main()

{

int x=1;

int y=2;

int &b=x;

printf("&x=%x,&y=%x,&b=%x,b=%x\n",&x,&y,&y-1,*(&y-1));

return 0;

}

我的結(jié)果是&x=bfe1b308,&y=bfe1b304,&b=bfe1b300,b=8048460

與原作者的不同,然后我就將上面的程序進(jìn)行編譯得到下面的結(jié)果:

00000000 <main>:

#include <cstdio>

int main()

{

0: 55 push %ebp

1: 89 e5 mov %esp,%ebp

3: 83 e4 f0 and $0xfffffff0,%esp

6: 83 ec 30 sub $0x30,%esp

int x=1;

9: c7 44 24 28 01 00 00 movl $0x1,0x28(%esp) 1賦值給x(x在堆棧的0x28處)

10: 00

int y=2;

11: c7 44 24 24 02 00 00 movl $0x2,0x24(%esp) 2賦值給y(y在堆棧的0x24處)

18: 00

int &b=x;

19: 8d 44 24 28 lea 0x28(%esp),%eax x的地址0x28 傳給寄存器%eax

1d: 89 44 24 2c mov %eax,0x2c(%esp) %eax的值賦給堆棧0x2c處(這兒比較重要)

printf("&x=%x,&y=%x,&b=%x,b=%x\n",&x,&y,&y-1,*(&y-1));

21: 8d 44 24 24 lea 0x24(%esp),%eax 將堆棧0x24處的地址傳給寄存器%eax

25: 83 e8 04 sub $0x4,%eax %eax的值減掉4

28: 8b 10 mov (%eax),%edx 將寄存器%eax中地址所指向的內(nèi)容傳給寄存器%edx

2a: 8d 44 24 24 lea 0x24(%esp),%eax 將堆棧0x24處的地址傳給寄存器%eax

2e: 83 e8 04 sub $0x4,%eax %eax的值減掉4

31: 89 54 24 10 mov %edx,0x10(%esp) %edx的內(nèi)容傳給堆棧0x10處

35: 89 44 24 0c mov %eax,0xc(%esp) %eax的內(nèi)容傳給堆棧0xc處

39: 8d 44 24 24 lea 0x24(%esp),%eax 將堆棧0x24處的地址傳給寄存器%eax

3d: 89 44 24 08 mov %eax,0x8(%esp) %eax的內(nèi)容傳給堆棧0x8處

41: 8d 44 24 28 lea 0x28(%esp),%eax 將堆棧0x28處的地址傳給寄存器%eax

45: 89 44 24 04 mov %eax,0x4(%esp) %eax的內(nèi)容傳給堆棧0x4處

49: c7 04 24 00 00 00 00 movl $0x0,(%esp)

50: e8 fc ff ff ff call 51 <main+0x51>

return 0;

55: b8 00 00 00 00 mov $0x0,%eax

}

5a: c9 leave

5b: c3 ret


上面基本每一句中都進(jìn)行解釋。

從這兒可以看到我的機(jī)器上生成的匯編代碼是將x的地址賦給了堆棧中地址所在處的下一個(gè)地址單元。

從printf所生成的匯編代碼處我們也可以看到,是按照逆序來(lái)計(jì)算的(&*(&y-1), &y-1, &y, x)這也印證了C標(biāo)準(zhǔn)中提到的函數(shù)的參數(shù)是逆序入棧的。

為了驗(yàn)證上面的想法,將原程序中的*(&y-1)改為*(&x+1)

結(jié)果為:

&x=bf9a74c8,&y=bf9a74c4,&b=bf9a74cc,b=bf9a74c8

這樣就和作者的符合起來(lái)了。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多