用彙編的眼光看C (開篇)

2021-05-28 08:40:17 字數 4216 閱讀 5376

很多朋友,包括我自己在內,對c++語言的很多特性不是很明白。特別是幾年前找工作的時候,為了應付來自工作單位的考試,我經常逼著自己的去記住一些複雜的試題和答案。可是常常時間已過,一切又回到了原點。原來沒有弄清楚的問題還是沒有弄明白,一切都沒有發生改變。直到若干年後,當我在編碼過程中不斷積累經驗,嘗試用彙編**和記憶體資料來解釋一些現象的時候,才明白有些東西其實並不複雜。也許有的朋友對組合語言會有畏懼,其實沒有必要。只要你對c語言有一些基礎,對堆疊有一些印象,那麼你已經擁有組合語言的基礎了。在接下來的數篇部落格中,我們就會就x86彙編、資料型別、資料執行邏輯、指標、資料、類、過載運算子在彙編下是如何展開的做一些介紹,談一些個人的看法。下面,我們就進行一些小測試,同時用組合語言來說明一下。大家可以一起做一下。

(1) char name 和 char* name

view plain

1:  

2:    void

process()  

3:    ;  

00401038   mov         eax,[string "hello"

(0042201c)]  

0040103d   mov         dword ptr [ebp-8],eax  

00401040   mov         cx,word ptr [string "hello"

+4 (00422020)]  

00401047   mov         word ptr [ebp-4],cx  

5:        char

* name_glb = 

"hello"

;  0040104b   mov         dword ptr [ebp-0ch],offset string "hello"

(0042201c)  

6:    }  

00401052   pop         edi  

00401053   pop         esi  

00401054   pop         ebx  

00401055   mov         esp,ebp  

00401057   pop         ebp  

00401058   ret  

通過上面的**,我們可以清楚地看出兩者之間的差別。"hello"字串是乙個全域性唯讀變數,空間位址為0x0042201c。name_tmp是函式內的char陣列,第4行語句下面四行表示全域性資料「hello」是分兩次拷貝到name_tmp的,第一次是dword、四個位元組,第二次是word、兩個位元組。所以name_tmp共有6個位元組。相比較而言,name_glb什麼也沒有,它只是把自己指向了全域性變數而已,所以它只是乙個指標而已。

view plain

class

;  

view plain

9:    

void

process()  

10:     

00401040   lea         ecx,[ebp-4]  

00401048   pop         edi  

00401049   pop         esi  

0040104a   pop         ebx  

0040104b   add         esp,44h  

0040104e   cmp         ebp,esp  

00401050   call        __chkesp (004010b0)  

00401055   mov         esp,ebp  

00401057   pop         ebp  

00401058   ret  

view plain

class

};  

view plain

10:   

void

process()  

11:     

0040104f   pop         edi  

00401050   pop         esi  

00401051   pop         ebx  

00401052   add         esp,40h  

00401055   cmp         ebp,esp  

00401057   call        __chkesp (004010e0)  

0040105c   mov         esp,ebp  

0040105e   pop         ebp  

0040105f   ret  

通過執行函式,我們發現沒有任何異常產生,為什麼呢?因為我們發現ecx是作為0傳給print函式的,也就是我們熟悉的this指標為0。但是我們發現在print函式內部沒有用到this指標,因為我們根本沒有對this->value進行訪問,只是乙個返回語句return。這說明指標作為class null指標並不可怕,可怕的是用null去訪問記憶體中的資料。

(4) int m = 1; int n = m++ + ++m; 那麼n是多少呢?

view plain

10:   

void

process()  

11:     

0040d50a   pop         edi  

0040d50b   pop         esi  

0040d50c   pop         ebx  

0040d50d   mov         esp,ebp  

0040d50f   pop         ebp  

通過彙編**,我們看到【ebp-4】就是m在堆疊中的位址,【ebp-8】就是n在堆疊中的位址。 int n = m++ + ++m下面總共有9句彙編。我們可以分析一下:前面三句表示m自己增加1,第四句表示ecx = m,即ecx = 2。第五句ecx和m相加,翻譯過來就是ecx = ecx + m。此時ecx = 4。第六句表示 n = ecx。 第七句到第九句表示m自增加1。為什麼會出現這樣的情況呢,其實道理很簡單,主要是因為我們的表示式是從右向左運算的。如果大家這樣看就明白了,首先++m,然後 n = m + m,最後 m++。

(5) *p++和(*p)++區別是什麼

view plain

10:   

void

process()  

11:     

0040d507   pop         edi  

0040d508   pop         esi  

0040d509   pop         ebx  

0040d50a   mov         esp,ebp  

0040d50c   pop         ebp  

0040d50d   ret  

我們首先建立區域性變數data。然後把data的指標複製給p。從彙編**可以清楚的看出來:*p++就等於p++;(*p)++首先把指標複製給edx,然後獲取edx位址指向的char資料複製給al,al自增加1,同時p位址複製給ecx,al複製給ecx指向的位址,就是這麼簡單。

(1) 下面的union在記憶體是怎麼安排的?gcc和vc編譯的時候,分配的記憶體size是一樣的嗎?

view plain

typedef

union

value;  

(2) 下面位址一致嗎?

view plain

char

value1 = ;  

char

value2 = ;  

char

* pvalue1 = 「hello」;  

char

* pvalue2 = 

"hello"

;  value1和value2位址一致嗎?pvalue1和pvalue2呢?  

(3)下面一段話為什麼執行錯誤?為什麼記憶體洩露了?怎麼修改?

view plain

class

(null != pname) free(pname);}  

};  

void

process()    

(全文完)

用彙編的眼光看C (開篇)

很多朋友,包括我自己在內,對c 語言的很多特性不是很明白。特別是幾年前找工作的時候,為了應付來自工作單位的考試,我經常逼著自己的去記住一些複雜的試題和答案。可是常常時間已過,一切又回到了原點。原來沒有弄清楚的問題還是沒有弄明白,一切都沒有發生改變。直到若干年後,當我在編碼過程中不斷積累經驗,嘗試用彙...

用彙編的眼光看C (開篇)

很多朋友,包括我自己在內,對c 語言的很多特性不是很明白。特別是幾年前找工作的時候,為了應付來自工作單位的考試,我經常逼著自己的去記住一些複雜的試題和答案。可是常常時間已過,一切又回到了原點。原來沒有弄清楚的問題還是沒有弄明白,一切都沒有發生改變。直到若干年後,當我在編碼過程中不斷積累經驗,嘗試用彙...

用彙編的眼光看C (開篇)

很多朋友,包括我自己在內,對c 語言的很多特性不是很明白。特別是幾年前找工作的時候,為了應付來自工作單位的考試,我經常逼著自己的去記住一些複雜的試題和答案。可是常常時間已過,一切又回到了原點。原來沒有弄清楚的問題還是沒有弄明白,一切都沒有發生改變。直到若干年後,當我在編碼過程中不斷積累經驗,嘗試用彙...