C/C++ 指標精髓)

來源:互聯網
上載者:User
C/C++指標精髓(一)

[摘要]

    指標是C和C++語言編程中最重要的概念之一,也是最容易產生困惑並導致程式出錯的問題之一。利用指標編程可以表示各種資料結構, 通過指標可使用主調函數和被調函數之間共用變數或資料結構,便於實現雙向資料通訊;並能像組合語言一樣處理記憶體位址,從而編出精練而高效的程式。指標極大地豐富了C和C++語言的功能。

    在本文中,主要分兩部分對指標進行討論。首先,基礎篇討論關於指標的內容和運算操作等,可以是讀者對指標的知識有一定瞭解和認識;隨後在使用篇中重點討論指標的各種應用,揭破指標在日常編程中的精髓,從而使讀者能夠真正地瞭解、認識和使用指標。

    第一篇:基礎篇

    1.1指標的概念

    談到指標,它的靈活性和難控制性讓許多程式員談虎色變;但它的直接操作記憶體,在資料

    操作方面有著速度快,節約記憶體等優點,又使許多C++程式員的深愛不以。那麼指標究竟是怎麼樣一個概念呢?

    其實, 指標就是一類變數,是一類包含了其他變數或函數的地址的變數。與其他變數所不同的是,一般的變數包含的是實際的真實的資料,而指標是一個指標,它告訴程式在記憶體的哪塊地區可以找到資料。

    好了,在這裡我們可以這樣定義指標:指標是一類包含了其他變數或函數的地址的變數,它裡面儲存的數值被解釋成為記憶體的地址。

    1.2指標的內容

    簡單講,指標有四個方面的內容:即指標的類型,指標所指向的類型,指標的值,指標本身所

    佔有的記憶體區。下面我們將分別闡述這些內容。

    1.2.1指標的類型

    從文法的角度看,指標的類型是指把指標聲明語句中的指標名字去掉所剩下的部分。這是指標本身所具有的類型。例如:

 int*ip;            //指標的類型是int*

char*ip;           //指標的類型是char*

int**ip;           //指標的類型是int**

int(*ip)[5];        //指標的類型是int(*)[5]

    1.2.2指標所指向的類型

    當你通過指標來訪問指標所指向的記憶體區時,指標所指向的類型決定了編譯器將把那片記憶體區裡的內容當做什麼類型來看待。從文法的角度看,指標所指向的類型是指標聲明語句中的指標名字和名字左邊的指標聲明符*去掉所剩下的部分。例如:

 int*ip;            //指標所指向的類型是int

char*ip;           //指標所指向的類型是char

int**ip;           //指標所指向的類型是int*

int(*ip)[5];        //指標所指向的類型是int()[5]

    1.2.3指標的值(或稱指標所指向的記憶體區)

    指標的值或者叫指標所指向的記憶體區或地址,是指標本身儲存的數值,這個值將被編譯器當作一個地址,而不是一個一般的數值。在32位程式裡,所有類型的指標的值都是一個32位整數,因為32位程式裡記憶體位址全都是32位長。 指標所指向的記憶體區就是從指標的值所代表的那個記憶體位址開始,長度為sizeof(指標所指向的類型)的一片記憶體區。以後,我們說一個指標的值是XX,就相當於說該指標指向了以XX為首地址的一片記憶體地區;我們說一個指標指向了某塊記憶體地區,就相當於說該指標的值是這塊記憶體地區的首地址。

    指標所指向的記憶體區和指標所指向的類型是兩個完全不同的概念。在上例中,指標所指向的類型已經有了,但由於指標還未初始化,所以它所指向的記憶體區是不存在的,或者說是無意義的。

    以後,每遇到一個指標,都應該問問:這個指標的類型是什嗎?指標指的類型是什嗎?該指標指向了哪裡?

    1.2.4指標本身所佔有的記憶體區

    指標本身所佔有的記憶體區是指標本身占記憶體的大小,這個你只要用函數sizeof(指標的

    類型)測一下就知道了。在32位平台裡,指標本身佔據了4個位元組的長度。

    指標本身佔據的記憶體這個概念在判斷一個指標運算式是否是左值時很有用。

C/C++指標精髓(二)

  1.3指標與記憶體管理

    利用指標你可以將資料寫入記憶體中的任意位置,但是,一旦你的程式中有一個野指標("wild“pointer),即指向一個錯誤位置的指標,你的資料就危險了—存放在堆中的資料可能會被破壞,用來管理堆的資料結構也可能會被破壞,甚至作業系統的資料也可能會被修改,有時,上述三種破壞情況會同時發生。所以合理的正確的分配指標的地址是非常重要的。

    1.3.1記憶體配置的方式

    記憶體配置方式有三種:

    (1)從靜態儲存地區分配。記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個運行期間都存在。例如全域變數,static變數。

    (2)在棧上建立。在執行函數時,函數內局部變數的儲存單元都可以在棧上建立,函數執行結束時這些儲存單元自動被釋放。棧記憶體配置運算內建於處理器的指令集中,效率很高,但是分配的記憶體容量有限。

    (3) 從堆上分配,亦稱動態記憶體分配。程式在啟動並執行時候用malloc或new申請任意多少的記憶體,程式員自己負責在何時用free或delete釋放記憶體。動態記憶體的生存期由我們決定,使用非常靈活,但問題也最多,以下我們重點講解動態記憶體分配。

    1.3.2 malloc/free 的使用要點

    malloc與free是C/C++語言的標準庫函數,它用於申請動態記憶體和釋放記憶體。

    函數malloc的原型如下:

    void * malloc(size_t size);

    用malloc申請一塊長度為length的整數類型的記憶體,程式如下:

    int *ip = (int *) malloc(sizeof(int) * length);

    我們應當把注意力集中在兩個要素上:“類型轉換”和“sizeof”。

    malloc函數傳回值的類型是void *,所以在調用malloc時要顯式地進行類型轉換,將void * 轉換成所需要的指標類型。

    malloc函數本身並不識別要申請的記憶體是什麼類型,它只關心記憶體的總位元組數。例如int變數在16位系統下是2個位元組,在32位下是4個位元組;而float變數在16位系統下是4個位元組,在32位下也是4個位元組。這個你可以用sizeof(類型)去測試。

    在malloc的“()”中使用sizeof運算子是良好的風格,但要當心有時我們會昏了頭,寫出 ip = malloc(sizeof(ip))這樣的程式來。

    函數free的原型如下:

    void free( void * memblock );

    為什麼free函數不象malloc函數那樣複雜呢?這是因為指標p的類型以及它所指的記憶體的容量事先都是知道的,語句free(p)能正確地釋放記憶體。如果p是NULL指標,那麼free對p無論操作多少次都不會出問題。如果p不是NULL指標,那麼free對p連續操作兩次就會導致程式運行錯誤。

    1.3.3 new/delete 的使用要點

    對於非內部資料類型的對象而言,光用maloc/free無法滿足動態對象的要求。對象在建立的同時要自動執行建構函式,對象在消亡之前要自動執行解構函式。由於malloc/free是庫函數而不是運算子,不在編譯器控制許可權之內,不能夠把執行建構函式和解構函式的任務強加於malloc/free.

    因此C++語言需要一個能完成動態記憶體分配和初始化工作的運算子new,以及一個能完成清理與釋放記憶體工作的運算子delete.注意new/delete不是庫函數,只是C++的運算子。我們來看如下例子就知道怎麼回事了。

 class Object

{

 public :

  Object(void){std::cout << “Initialization”<< std::endl; }

  ~Object(void){std::cout << “Destroy”<< std::endl; }

  void Initialize(void){std:: cout << “Initialization”<< std::endl; }

  void Destroy(void){ std::cout << “Destroy”<< std::endl; }

}

void UseMallocFree(void)

{

 Object *ip = (Object *)malloc(sizeof(Object));    // 申請動態記憶體

 ip->Initialize();                             // 初始化

 //…

 ip->Destroy();                              // 清除工作

 free(ip);                                   // 釋放記憶體

}

void UseNewDelete(void)

{

 Object *ip = new Object;                     // 申請動態記憶體並且初始化

 //…

 Delete ip;                                  // 清除並且釋放記憶體

}

    用malloc/free和new/delete如何?對象的動態記憶體管理

    類Object的函數Initialize類比了建構函式的功能,函數Destroy類比了解構函式的功能。函數UseMallocFree中,由於malloc/free不能執行建構函式與解構函式,必須調用成員函數Initialize和Destroy來完成初始化與清除工作。函數UseNewDelete則簡單得多。

    所以我們不要企圖用malloc/free來完成動態對象的記憶體管理,應該用new/delete.由於內部資料類型的“對象”沒有構造與析構的過程,對它們而言malloc/free和new/delete是等價的。new內建了sizeof、類型轉換和型別安全檢查功能, ,對於非內部資料類型的對象而言,new在建立動態對象的同時完成了初始化工作。

    new/delete 常使用的方法如下:

    typeof *ip = new typeof[length];

    類/結構 *ip = new 類結構;

    一般釋放如下:delete ip;

    數組的釋放如下:delete [] ip;

    1.3.4記憶體耗盡怎麼辦?

    如果在申請動態記憶體時找不到足夠大的記憶體塊,malloc和new將返回NULL指標,宣告記憶體申請失敗。通常有三種方式處理“記憶體耗盡”問題。

    (1)判斷指標是否為NULL,如果是則馬上用return語句終止本函數。例如:

 void Func(void)

{

 A *a = new A;

 if(a == NULL)

 {

  return;

 }

 …

}

    (2)判斷指標是否為NULL,如果是則馬上用exit(1)終止整個程式的運行。例如:

 void Func(void)

{

 A *a = new A;

 if(a == NULL)

 {

  std::cout << “Memory Exhausted” << std::endl;

  exit(1);

 }

 …

}

 

    (3)為new和malloc設定異常處理函數。例如Visual C++可以用_set_new_hander函數為new設定使用者自己定義的異常處理函數,也可以讓malloc享用與new相同的異常處理函數。詳細內容請參考C++使用手冊。

    有一個很重要的現象要告訴大家。對於32位以上的應用程式而言,無論怎樣使用malloc與new,幾乎不可能導致“記憶體耗盡”。因為32位作業系統支援“虛存”,記憶體用完了,自動用硬碟空間頂替。我不想誤導讀者,必須強調:不加錯誤處理將導致程式的品質很差,千萬不可因小失大。

    1.3. 5杜絕野指標

    “野指標”不是NULL指標,是指向“垃圾”記憶體的指標。人們一般不會錯用NULL指標,因為用if語句很容易判斷。但是“野指標”是很危險的,if語句對它不起作用。 “野指標”的原因主要有如下幾種:

    (1)指標變數沒有被初始化。任何指標變數剛被建立時不會自動成為NULL指標,它的預設值是隨機的,它會亂指一氣。所以,指標變數在建立的同時應當被初始化,要麼將指標設定為NULL,要麼讓它指向合法的記憶體。例如

    char *ip = NULL;

    char *ip = new char;

    (2)指標ip被free或者delete之後,沒有置為NULL,讓人誤以為ip是個合法的指標。

    (3)指標操作超越了變數的作用範圍。這種情況讓人防不勝防,樣本程式如下:

 class A

{

 public:

  void Func(void){ std::cout << “Func of class A” << std::endl; }

};

void Test(void)

{

 A *p;

 {

  A a;

  p = &a; // 注意 a 的生命期

 }

 p->Func(); // p是“野指標”

}

    函數Test在執行語句p->Func()時,對象a已經消失,而p是指向a的,所以p就成了“野指標”。但奇怪的是有些編譯器運行這個程式時居然沒有出錯,這可能與編譯器有關。

    1.3.6指標參數是如何傳遞記憶體的?

    如果函數的參數是一個指標,不要指望用該指標去申請動態記憶體。見如下例子:

 void GetMemory(char *ip, int num)

{

 ip = (char *)malloc(sizeof(char) * num);

}

void Test(void)

{

 char *str = NULL;

 GetMemory(str, 100); // str 仍然為 NULL

 strcpy(str, "hello"); // 運行錯誤

}

    試圖用指標參數申請動態記憶體

    毛病出在函數GetMemory中。編譯器總是要為函數的每個參數製作臨時副本,指標參數ip的副本是 _ip,編譯器使 _ip = ip.如果函數體內的程式修改了_ip的內容,就導致參數ip的內容作相應的修改。這就是指標可以用作輸出參數的原因。在本例中,_ip申請了新的記憶體,只是把_ip所指的記憶體位址改變了,但是ip絲毫未變。所以函數GetMemory並不能輸出任何東西。事實上,每執行一次GetMemory就會泄露一塊記憶體,因為沒有用free釋放記憶體。

    如果非得要用指標參數去申請記憶體,那麼應該改用“指向指標的指標(二級指標)”,見如下樣本:

 void GetMemory(char **p, int num)

{

 *ip = (char *)malloc(sizeof(char) * num);

}

void Test(void)

{

 char *str = NULL;

 GetMemory(&str, 100); // 注意參數是 &str,而不是str

 strcpy(str, "hello");

 std::cout<< str << std::endl;

 free(str);

}

    用指向指標的指標申請動態記憶體

    當然,我們也可以用函數傳回值來傳遞動態記憶體。這種方法更加簡單,見如下樣本:

 char *GetMemory(int num)

{

 char *ip = (char *)malloc(sizeof(char) * num);

 return ip;

}

void Test(void)

{

 char *str = NULL;

 str = GetMemory(100);

 strcpy(str, "hello");

 std::cout<< str << std::endl;

 free(str);

}

    用函數傳回值來傳遞動態記憶體

    用函數傳回值來傳遞動態記憶體這種方法雖然好用,但是常常有人把return語句用錯了。這裡強調不要用return語句返回指向“棧記憶體”的指標,因為該記憶體在函數結束時自動消亡,見如下樣本:

 char *GetString(void)

{

 char p[] = "hello world";

 return p; // 編譯器將提出警告

}

void Test(void)

{

 char *str = NULL;

 str = GetString(); // str 的內容是垃圾

 std::cout<< str << std::endl;

}

    return語句返回指向棧記憶體的指標

    最後,根據以上闡述,我們總結如下使用規則供大家參考:

    【規則1】用malloc或new申請記憶體之後,應該立即檢查指標值是否為NULL.防止使用指標值為NULL的記憶體。

    【規則2】不要忘記為數組和動態記憶體賦初值。防止將未被初始化的記憶體作為右值使用。

    【規則3】避免數組或指標的下標越界,特別要當心發生“多1”或者“少1”操作。

    【規則4】動態記憶體的申請與釋放必須配對,防止記憶體流失。

    【規則5】用free或delete釋放了記憶體之後,立即將指標設定為NULL,防止產生“野指標”。

 

C/C++指標精髓(三)

1.4指標的運算

  1.4.1賦值運算

  指標變數的賦值運算有以下幾種形式:

  1.4.1.1指標變數初始化賦值如下:

  int a;

  int *ip=&a;

  1.4.1.2 把一個變數的地址賦予指向相同資料類型的指標變數。例如:

  int a;

  int *ip;

  ip=&a;             //把整型變數a的地址賦予整型指標變數ip

  1.4.1.3把一個指標變數的值賦予指向相同類型變數的另一個指標變數。例如:

  int a;

  int *pa=&a;

  int *PB;

  pb=pa;              //把a的地址賦予指標變數pb

  由於pa,pb均為指向整型變數的指標變數,因此可以相互賦值。

  1.4.1.4把數組的首地址賦予指向數組的指標變數。例如:

  int a[5],*pa;

  pa=a;               //數組名表示數組的首地址,故可賦予指向數組的指標變數pa

  也可寫為:

  pa=&a[0];           //數組第一個元素的地址也是整個數組的首地址也可賦予pa

  當然也可採取初始化賦值的方法:

  int a[5],*pa=a;

  以上是一些基本的數組賦值方法,後面我們會詳細討論指標在數組中的使用。

  1.4.1.5把字串的首地址賦予指向字元類型的指標變數。例如:

  char *pc;

  pc="c language";

  或用初始化賦值的方法寫為:

  char *pc=" c language ";

  這裡應說明的是並不是把整個字串裝入指標變數, 而是把存放該字串的字元數組的首地址裝入指標變數。

  1.4.1.6把函數的入口地址賦予指向函數的指標變數。例如:

  int (*pf)();

  pf=f;                //f為函數名

  1.4.2加減運算

  對於指向數組的指標變數,可以加上或減去一個整數n.設ip是指向數組a的指標變數,則ip+n,ip-n,ip++,++ip,ip——,——ip 運算都是合法的。指標變數加或減一個整數n的意義是把指標指向的當前位置(指向某數組元素)向前或向後移動n個位置。應該注意,數組指標變數向前或向後移動一個位置和地址加1或減1 在概念上是不同的。因為數組可以有不同的類型, 各種類型的數組元素所佔的位元組長度是不同的。如指標變數加1,即向後移動1 個位置表示指標變數指向下一個資料元素的首地址。而不是在原地址基礎上加1.看如下例子:

 char a[20];
  int*ip=a;
  ...
  ip++;

  在上例中,指標ip的類型是int*,它指向的類型是int,它被初始化為指向整形變數a.接下來的第3句中,指標ip被加了1,編譯器是這樣處理的:它把指標ip的值加上了sizeof(int),在32位程式中,是被加上了4.由於地址是用位元組做單位的,故ip所指向的地址由原來的變數a的地址向高地址方向增加了4個位元組。

  由於char類型的長度是一個位元組,所以,原來ptr是指向數組a的第0號單元開始的四個位元組,此時指向了數組a中從第4號單元開始的四個位元組。再看如下例子:

   char a[20];
  int*ip=a;
  ...
  ip+=5;

  在這個例子中,ip被加上了5,編譯器是這樣處理的:將指標ip的值加上5乘sizeof(int),在32位程式中就是加上了5乘4=20.由於地址的單位是位元組,故現在的ip所指向的地址比起加5後的ip所指向的地址來說,向高地址方向移動了20個位元組。在這個例子中,沒加5前的ip指向數組a的第0號單元開始的四個位元組,加5後,ptr已經指向了數組a的合法範圍之外了。雖然這種情況在應用上會出問題,但在文法上卻是可以的。這也體現出了指標的靈活性。

  如果上例中,ip是被減去5,那麼處理過程大同小異,只不過ip的值是被減去5乘sizeof(int),新的ip指向的地址將比原來的ip所指向的地址向低地址方向移動了20個位元組。

  總結一下,一個指標ipold加上一個整數n後,結果是一個新的指標ipnew,ipnew的類型和ipold的類型相同,ipnew所指向的類型和ipold所指向的類型也相同。ipnew的值將比ipold的值增加了n乘sizeof(ipold所指向的類型)個位元組。就是說,ipnew所指向的記憶體區將比ipold所指向的記憶體區向高地址方向移動了n乘sizeof(ipold所指向的類型)個位元組。

  一個指標ipold減去一個整數n後,結果是一個新的指標ipnew,ipnew的類型和ipold的類型相同,ipnew所指向的類型和ipold所指向的類型也相同。ipnew的值將比ipold的值減少了n乘sizeof(ipold所指向的類型)個位元組,就是說,ipnew所指向的記憶體區將比ipold所指向的記憶體區向低地址方向移動了n乘sizeof(ipold所指向的類型)個位元組。

1.4.3關係運算

  指向同一個數組中的不同元素的兩個指標可以進行各種關係運算。例如:

  ip1==ip2表示ip1和ip2指向同一數組元素

  ip1>ip2表示ip1處於高地址位置

  ip1<IP2表示IP2處於低地址位置< p>

  指標變數還可以與0比較。設ip為指標變數,則ip==0表明ip是null 指標,它不指向任何變數;ip!=0表示ip不是null 指標。null 指標是由對指標變數賦予0值而得到的。例如:

  #define NULL 0

  int *ip=NULL;

  對指標變數賦0值和不賦值是不同的。指標變數未賦值時,可以是任意值,是不能使用的。否則將造成意外錯誤。而指標變數賦0值後,則可以使用,只是它不指向具體的變數而已。

  1.4.4取地址運算子‘&’和取內容運算子‘*’

  取地址運算子&是單目運算子,其結合性為自右至左,其功能是取變數的地址。

  取內容運算子*是單目運算子,其結合性為自右至左,用來表示指標變數所指的變數。在*運算子之後跟的變數必須是指標變數。需要注意的是指標運算子*和指標變數說明中的指標說明符* 不是一回事。在指標變數說明中,‘*’是類型說明符,表示其後的變數是指標類型。而運算式中出現的‘*’則是一個運算子用以表示指標變數所指的變數。如下例子:

      int a=12;
  int b;
  int *p;
  int **ptr;
  p=&a;   //&a的結果是一個指標,類型是int*,指向的類型是int,指向的地址是a的
  //地址。
  *p=24;   //*p的結果,在這裡它的類型是int,它所佔用的地址是p所指向的地址。
  ptr=&p; //&p的結果是個指標,該指標的類型是p的類型加個*,在這裡是int **。該
  //指標所指向的類型是p的類型,這裡是int*。該指標所指向的地址就是指標
  //p自己的地址。
  *ptr=&b;//*ptr是個指標,&b的結果也是個指標,且這兩個指標的類型和所指向的類型//是一樣的,所以用&b來給*ptr賦值就是毫無問題的了。
  **ptr=34;//*ptr的結果是ptr所指向的東西,在這裡是一個指標,對這個指標再做一次*
  //運算,結果就是一個int類型的變數。

  1.4.5關於括弧組合

  在解釋組合說明符時, 標識符右邊的方括弧和圓括弧優先於標識符左邊的“*”號,而方括弧和圓括弧以相同的優先順序從左至右結合。但可以用圓括弧改變約定的結合順序。

  閱讀組合說明符的規則是“從裡向外”。從標識符開始,先看它右邊有無方括弧或園括弧,如有則先作出解釋,再看左邊有無*號。如果在任何時候遇到了閉括弧,則在繼續之前必須用相同的規則處理括弧內的內容。

  1.5指標運算式

  一個運算式的最後結果如果是一個指標,那麼這個運算式就叫指標表式。所以指標運算式也具有指標所具有的四個要素:指標的類型,指標所指向的類型,指標指向的記憶體區,指標自身佔據的記憶體。

 

 

C/C++指標精髓(四)

需要首先明確的是C語言中的指標是一種資料類型,單從這點上來看,指標和int,float什麼的沒什麼大區別,但是指標這種資料類型所儲存的東西可就大有講究了。

我們都清楚,程式是要在記憶體中執行的,在記憶體中執行的話就必須得有個確認位置的方法,這個方法就是記憶體的地址。也就是說,一個運行中的程式中的所有的“組件”(指令,資料什麼的),都在記憶體中放著呢,也就是他們都有地址。那麼指標呢,就是專門用來存放這些記憶體位址的一種變數。因為它存放的值是地址,這就導致了指標變數和其他類型變數有很大的不同。

指標具有兩大特點,一是指標變數本身的值。這個值剛才說了,就是別的資料的地址(變數)或者指令的地址(函數),這個地址可以理解為一種間接訪問他人的“線索”,也就是說,通過指標變數中存放的地址,可以訪問某些資料或者實現指令的跳轉。既然是變數,那肯定有自己儲存空間的大小了。那麼指標變數的大小是多少呢?這個非常簡單:指標是用來存放地址的,那麼指標變數的長度當然就是其所在機器地址匯流排的長度了!比如在32位的x86上,指標一般來說就是32位的,也就是4個位元組大小。二就是指標變數的類型。我們都知道再聲明指標變數的時候都得指名類型,比如char */int *等,那麼這裡的char/int之類的又有什麼含義呢?我認為可以把這些類型理解成一種“尺度”,或者說是一種“許可權”。如剛才所說,指標的值指明了可以訪問的位置,那麼指標的類型則限制了指標從該位置所能訪問的長度。比如,
int *p 說明了p這個指標變數在使用諸如*p等方式訪問p中存放的地址中的資料的時候,系統會提取sizeof(int)長度的資料出來,這個資料當然就是一個int型的值了,或者在進行諸如p++這樣的操作時,系統會加上sizeof(int)的長度。總而言之,指標其實就是一個地址加一個長度,地址限定了指標起始的位置,而長度則規定了系統按什麼方式來解析記憶體的資料。

有了以上的說明,下面將要嘮叨的有關於指標強制類型轉換的概念就很好理解了

指標的強制類型轉換很好理解,轉換的就是指標的類型,也就是系統使用指標的方式。比如從int*轉換到char*,那麼指標中的地址並沒有變化,只是類型變了,比如在執行p++的時候,原來是增加4個位元組(32位int),而轉換之後便成了增加1個位元組(char的長度)。再複雜一點,常規類型向結構體的強制類型轉換也是這樣的道理。在Linux的網路編程中常常用到將某種指標強制轉換成某種協議包頭的struct,然後提取該包頭的資料(由於最近小研究了一下pcap,對這個比較熟悉^_^)或者各種結構體的指標之間進行類型轉換。這些轉換其實都是在改變系統對從某一地址開始的一堆資料的不同的解釋方法。比如在pcap中,抓取的乙太網路資料包(當然在記憶體中存在)的地址被存放到了一個u_char類型的指標中,這個指標其實就是一個存有整個資料包所有資訊的一塊記憶體空間的開始地址!但是如果使用char類型的指標來一個位元組一個位元組的訪問該記憶體段當然無法取得正確的資料,所以要把u_char轉換成相應的網路通訊協定的struct,然後按順序訪問,具體就不贅述了。類似的例子還有很多,但我認為如果仔細研究一下pcap庫提取資料的方式,會對指標強制類型轉換的理解有很大的協助。

總之呢,指標也並非什麼神秘之物,也沒有人們說的那麼難懂,關鍵就是一個地址的問題。我倒是認為C語言最閃光,最強大,最靈活的特性就是指標了。指標,C之魂也~!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.