程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 用匯編的眼光看C++(之指針2)

用匯編的眼光看C++(之指針2)

編輯:C++入門知識

 

【 聲明:版權所有,歡迎轉載,請勿用於商業用途。  聯系信箱:feixiaoxing @163.com】

 

 

 

    (4)指針和引用

 

    引用是C++和語言的區別之一。其實本質上說兩者是一致的。朋友們可以看下面兩段代碼。

 

    a)指針和指針的函數代碼

 

 

void add_point(int* q) 

    (*q)++; 

 

void add_ref(int& q) 

    q++; 

 

    b)函數的調用代碼

 

 

56:       int m = 10; 

004012E8   mov         dword ptr [ebp-4],0Ah 

57:       add_point(&m); 

004012EF   lea         eax,[ebp-4] 

004012F2   push        eax 

004012F3   call        @ILT+45(process) (00401032) 

004012F8   add         esp,4 

58:       add_ref(m); 

004012FB   lea         ecx,[ebp-4] 

004012FE   push        ecx 

004012FF   call        @ILT+50(add_ref) (00401037) 

00401304   add         esp,4 

59:       return 1; 

00401307   mov         eax,1 

60:   } 

    分析一下,我們發現其實函數add_point和函數add_ref實現的功能,都是對輸入的數據進行自增處理。只不過處理的時候,一個函數的入參是指針,一個函數的入參是引用。在函數調用的地方,大家可以發現指針和引用居然是一樣的。首先看add_point,第一句獲取m的地址復制給eax,第二句壓棧處理,第三句調用函數add_point,第四句出棧回溯。同樣看一下add_ref,第一句獲取m的地址復制給ecx,第二句ecx壓棧處理,第三句調用函數add_ref,第四句堆棧回溯處理。相信看到這裡,大家就明白C++的前輩們為什麼鼓勵大家多多使用引用了。

 

    (5)指針和結構體

 

    我們在學習數據節點的時候,相信大家都學習過這樣的一個數據結構定義:

 

 

typedef struct _NODE 

    int data; 

    struct _NODE* next; 

}NODE; 

    當時,我們都不明白這個結構體是什麼意思?其實這個定義完全修改成這樣:

 

 

typedef struct _NODE 

    int data; 

    void* next; 

}NODE; 

     這兩個數據結構體其實是完全一致的。第一個數據保存數據,第二個數據為指針,內容為某一個數據類型的地址。這種確定的地址和void*類型的地址類型是一樣的。只不過前面一種更加直接。後面一種地址的固然方便,但是使用的時候每一次都需要進行轉換,很是麻煩。如果大家感興趣,不妨是接著看下面一道題目:

 

 

typedef struct _NODE 

    struct _NODE* next; 

}NODE; 

    我們既可以把節點NODE的地址看是NODE*,也可以堪稱是NODE**,兩者之間有差別嗎?(其實沒有區別)

 

    linux 內核代碼上面有一種計算偏移值的方法,大家可以參考一下:

 

 

int offset = (int)&(((NODE*)(0))->next); 

    (6)class指針

 

     class指針比較復雜,不過大家可以從一個小范例看出一些端倪:

 

 

class fruit 

public: 

    fruit() {} 

    ~fruit() {} 

    void print() {printf("fruit!\n");} 

}; 

 

class apple : public fruit 

public: 

    apple() {} 

    ~apple() {} 

    void print() {printf("apple!\n");} 

}; 

 

void process() 

    fruit f; 

    apple* a = (apple*)&f; 

    a->print(); 

    fruit* b = &f; 

    b->print(); 

    熟悉C++的朋友可以很快知道這道題目的答案了,那麼為什麼a和b都指向同一個地址,使用了相同的print函數,但是結果不同。我想這主要是因為兩者數據類型不同的緣故。一旦指針和某一種數據類型綁在了一起,那麼這個指針的所有行為事實上都已經被這種類型的數據所限定了。普通數據類型是這樣,自定義的class類型也是這樣。只要不是在print函數前面加上virtual,我們就會發現兩個print的調用都是硬編碼,和普通的函數調用無異。所以說,指針離不開數據類型,離開具體類型的地址是沒有意義的。就像void*是可以原諒的,但是void卻是萬萬不能接受的。下面的匯編代碼很好的說明了這一點。

 

 

65:       fruit f; 

0040132D   lea         ecx,[ebp-10h] 

00401330   call        @ILT+35(fruit::fruit) (00401028) 

00401335   mov         dword ptr [ebp-4],0 

66:       apple* a = (apple*)&f; 

0040133C   lea         eax,[ebp-10h] 

0040133F   mov         dword ptr [ebp-14h],eax 

67:       a->print(); 

00401342   mov         ecx,dword ptr [ebp-14h] 

00401345   call        @ILT+0(apple::print) (00401005) 

68:       fruit* b = &f; 

0040134A   lea         ecx,[ebp-10h] 

0040134D   mov         dword ptr [ebp-18h],ecx 

69:       b->print(); 

00401350   mov         ecx,dword ptr [ebp-18h] 

00401353   call        @ILT+25(fruit::print) (0040101e) 

 

 

(全文完)

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