天天看點

C指針通過編譯器實作彙編語言類似記憶體間接尋址功能,程式設計語言的差異主要來自編譯器的解釋

c指針通過編譯器實作彙編類似記憶體間接尋址功能,

程式設計語言的差異主要來自編譯器的解釋吧。

擔心圖檔久了失效,我都重新轉存的哈,希望造福對此感興趣的同學們。。

參考url:

<a href="http://bbs.pediy.com/showthread.php?t=105674">http://bbs.pediy.com/showthread.php?t=105674</a>

<a href="http://blog.csdn.net/willjet/article/details/5792689">http://blog.csdn.net/willjet/article/details/5792689</a>

<a href="http://bbs.21ic.com/icview-45502-1-1.html">http://bbs.21ic.com/icview-45502-1-1.html</a>

作者:亂雪 來源:hi.baidu.com/lu4nx

r.e.c--f22叫我來篇稿,我實在不知道寫啥,也很久沒寫過技術方面的東西了,剛看書時突然想到了寫指針,所有的c語書上都把指針描述得很抽象,是以,老規矩,結合調試器+彙編來了解它。

其實指針和彙編中的間接尋址很像,抽象點說,運用指針可以間接性地通路某變量内容。我說得太抽象了,扔代碼上來吧:

代碼:

這時lx的值就是10,可以加句printf("%d \n",lx);看到。

好了,進調試器來解釋吧。編譯環境vc6.0,調試器是vc6.0預設的調試器。需要觀衆有彙編語言的基礎。

先在int count = 10,lx;處下個斷點(右鍵——“insert/remove breakpoint”),然後按f5,進入調試狀态後,會自動在斷點處停下來,此時點在斷點行處右鍵——“go to disassembly”,來到彙編視窗。

這個時候斷點處的代碼如下:

0a是10的16進制,ebp-4是第一個變量count的位址,mov是傳送指令。此句的意思是把10指派給ebp-4,即count = 10。為了證明這一點,按一下f10單步運作,然後打開watch視窗,在“名稱”裡鍵入&amp;count,就看到了count的位址,如圖:

C指針通過編譯器實作彙編語言類似記憶體間接尋址功能,程式設計語言的差異主要來自編譯器的解釋

在這裡順便說下,我們定義了三個變量,分别是count、lx和pointer,那麼分别對應的位址是ebp-4、ebp-8、ebp-0ch,因為整型變量占4位元組記憶體,每次ebp都會減少4。

根據剛才代碼中的順序,下一句是pointer = &amp;count;,将pointer指向count的記憶體位址,我們看對應的彙編代碼:

前面說過,ebp-4是count變量的記憶體位址,為了直覺點,我把上面的彙編代碼改一下:

lea指令就是把一個記憶體變量有效的位址傳送給指定的寄存器。第一句lea   eax,[ebp-4]是把count的位址傳到eax寄存器,根據剛才在watch視窗中看到的count位址是0012ff7c,那麼eax裡面的值就是0012ff7c。第二句mov   dword ptr [ebp-0ch],eax是把eax中的值傳到ebp-0ch(pointer)中。很明顯,pointer = &amp;count;這句代碼就等同于pointer = 0012ff7c;。為了直覺點,打開registers視窗,觀察寄存器。按三下f10執行三次單步運作,執行完mov dword ptr [ebp-0ch],eax指令,這個時候寄存器内容如圖:

C指針通過編譯器實作彙編語言類似記憶體間接尋址功能,程式設計語言的差異主要來自編譯器的解釋

看eax的值正好是count變量的記憶體位址0012ff7c。然後在watch視窗中輸入pointer,可以看到pointer的内容是0x0012ff7c,如圖:

C指針通過編譯器實作彙編語言類似記憶體間接尋址功能,程式設計語言的差異主要來自編譯器的解釋

說明pointer已經指到了count的記憶體位址了。

接下來看下一句代碼lx = *pointer;,對應的彙編代碼如下:

為了直覺,我改一下代碼:

這行代碼意思是取出pointer指向的内容賦給變量lx。彙編代碼中第一句mov   ecx,dword ptr [ebp-0ch]意思是把ebp-0ch(pointer)中的值傳入到ecx寄存器中,剛才已經知道,pointer的内容是0x0012ff7c,那麼這個時候ecx的值就是0x0012ff7c。第二句mov edx,dword ptr [ecx]意思是取出ecx寄存器中的位址對應的值放入edx寄存器,此時edx寄存器的值就是count的值10了。最後再mov dword ptr [ebp-8],edx,把edx寄存器中的值傳到變量lx中。為了觀察到整個過程,我們單步運作,直到運作完mov edx,dword ptr [ecx]這句指令,然後觀察registers視窗,看到ecx的值是0012ff7c。ecx的值已經是count的記憶體位址了:

C指針通過編譯器實作彙編語言類似記憶體間接尋址功能,程式設計語言的差異主要來自編譯器的解釋

再單步運作一次,運作完指令mov edx,dword ptr [ecx],再看registers視窗,edx的值是0000000a,a是10的十六進制表示。

C指針通過編譯器實作彙編語言類似記憶體間接尋址功能,程式設計語言的差異主要來自編譯器的解釋

簡單總結下就是:首先将變量的位址放入寄存器中,然後再取出寄存器中存放位址對應的值。c指針的内幕就這樣,自己跟着調試一次代碼,就會了解了。如有不足之處歡迎一起讨論:)

c語言中指針和數組在編譯時的差別 例如:int a[10]和int *b,a[5]和*(b+5) 在編譯的時候差別是什麼? 經過反複的查資料和請教他人,最後在《c專家程式設計》裡找到了很好的答案。

    1。編譯器對數組名和指針變量的處理方式 編譯器在編譯時會産生一個符号表,記錄了符号名和它的位址。對于指針變量,這顯然很好了解。而數組名就不那麼明顯了,它僅僅是一個符号而已,何來位址?編譯器是這樣處理的,它記錄了array[0]的位址;這和我們通常的了解也是一樣的。    

2。帶下标形式的數組和指針尋址方式      

   (1)數組情形            

char a[9]="abcdefgh";             ...             c=a[i];

            在編譯期,會在符号表中建立這樣一條記錄:             name:a  address:9980             要擷取a[i]的值分兩個步驟:            

step 1:取得i的值并和9980相加            

step 2:在記憶體位址(9980+i)處取其内容 

        (2)指針情形            

char* p="abcdefgh";             ...             c=p[i];            

在編譯期,會在符号表中建立這樣一條記錄:             name:p  address:4624             要擷取p[i]的值分三個步驟:            

step 1:在記憶體位址4624處取其内容,比如說“5081”            

step 2:取得i的值并和5081相加             step 3:在記憶體位址(5081+i)取其内容

一、預備知識—程式的記憶體配置設定    一個由c/c++編譯的程式占用的記憶體分為以下幾個部分   

1、棧區(stack)—   由編譯器自動配置設定釋放   ,存放函數的參數值,局部變量的值等。其    操作方式類似于資料結構中的棧。   

2、堆區(heap)   —   一般由程式員配置設定釋放,   若程式員不釋放,程式結束時可能由os回    收   。注意它與資料結構中的堆是兩回事,配置設定方式倒是類似于連結清單,呵呵。   

3、全局區(靜态區)(static)—,全局變量和靜态變量的存儲是放在一塊的,初始化的    全局變量和靜态變量在一塊區域,   未初始化的全局變量和未初始化的靜态變量在相鄰的另    一塊區域。   -   程式結束後由系統釋放。   

4、文字常量區   —常量字元串就是放在這裡的。   程式結束後由系統釋放   

5、程式代碼區—存放函數體的二進制代碼。

二、例子程式     

這是一個前輩寫的,非常詳細     

//main.cpp     

int   a   =   0;   全局初始化區     

char   *p1;   全局未初始化區     

main()     

{     

int   b;   棧     

char   s[]   =   "abc";   棧     

char   *p2;   棧     

char   *p3   =   "123456";  123456/0在常量區,p3在棧上。     

static   int   c   =0;   全局(靜态)初始化區     

p1   =   (char   *)malloc(10);     

p2   =   (char   *)malloc(20);      配置設定得來得10和20位元組的區域就在堆區。     

strcpy(p1,   "123456");   123456/0放在常量區,編譯器可能會将它與p3所指向的"123456"    優化成一個地方。   

  }   

2.6存取效率的比較        

char   s1[]   =   "aaaaaaaaaaaaaaa";     

char   *s2   =   "bbbbbbbbbbbbbbbbb";     

aaaaaaaaaaa是在運作時刻指派的;     

而bbbbbbbbbbb是在編譯時就确定的;     

但是,在以後的存取中,在棧上的數組比指針所指向的字元串(例如堆)快。      比如:     

#include     

void   main()     

char   a   =   1;     

char   c[]   =   "1234567890";     

char   *p   ="1234567890";     

a   =   c[1];     

a   =   p[1];     

return;     

}     

對應的彙編代碼     

10:   a   =   c[1];      00401067   8a   4d   f1   mov   cl,byte   ptr   [ebp-0fh]      0040106a   88   4d   fc   mov   byte   ptr   [ebp-4],cl     

11:   a   =   p[1];      0040106d   8b   55   ec   mov   edx,dword   ptr   [ebp-14h]      00401070   8a   42   01   mov   al,byte   ptr   [edx+1]      00401073   88   45   fc   mov   byte   ptr   [ebp-4],al     

第一種在讀取時直接就把字元串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到    edx中,再根據edx讀取字元,顯然慢了。   

回複 #1 specter117 的文章

不知道你想要的是不是這個:

(1)對于順序通路的而言,數組比連結清單效率高。

(2)對于數組,間接通路(指數組名+下标)絕不比指針通路的效率高。

比如:

int i; int b[100];

數組通路;  

for(i = 0; i &lt; 100; i++)  

{    

b[i] = 0;

  }

指針通路:  

  int *p = b;   

{        

*p++ = 0;  

}

數組通路時,b等價于*(b + i) =*( (char *)b + i * sizeof(int) ) 指針通路時,*p++等價于*(p + 1) = *((char *)p + 1 * sizeof(int))

由于sizeof是在編譯時确定的,是以 b = *((char *) b + 4 * i) 而 *p++ = *((char *)p + 1) 間接通路有乘法運算,而指針通路隻有加法運算,

是以在這裡,間接通路沒有指針通路效率高。

繼續閱讀