【 聲明:版權所有,歡迎轉載,請勿用於商業用途。 聯系信箱: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)
(全文完)