程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 秘訣!教你閱讀復雜的C/C++聲明

秘訣!教你閱讀復雜的C/C++聲明

編輯:C++入門知識

曾經碰到過讓你迷惑不解、類似於int * (* (*fp1) (int) ) [10];這樣的變量聲明嗎?本文將由易到難,一步一步教會你如何理解這種復雜的C/C++聲明。

我們將從每天都能碰到的較簡單的聲明入手,然後逐步加入const修飾符和typedef,還有函數指針,最後介紹一個能夠讓你准確地理解任何C/C++聲明的“右左法則”。

需要強調一下的是,復雜的C/C++聲明並不是好的編程風格;我這裡僅僅是教你如何去理解這些聲明。

讓我們從一個非常簡單的例子開始,如下:

  1. int n; 

這個應該被理解為“declare n as an int”(n是一個int型的變量)。接下去來看一下指針變量,如下:

  1. int *p; 

這個應該被理解為“declare p as an int *”(p是一個int *型的變量),或者說p是一個指向一個int型變量的指針。我想在這裡展開討論一下:我覺得在聲明一個指針(或引用)類型的變量時,最好將*(或&)寫在緊靠變量之前,而不是緊跟基本類型之後。這樣可以避免一些理解上的誤區,比如:

再來看一個指針的指針的例子:

  1. char **argv; 

理論上,對於指針的級數沒有限制,你可以定義一個浮點類型變量的指針的指針的指針的指針,再來看如下的聲明:

  1. int RollNum[30][4]; int (*p)[4]=RollNum; int *q[5]; 

這裡,p被聲明為一個指向一個4元素(int類型)數組的指針,而q被聲明為一個包含5個元素(int類型的指針)的數組。另外,我們還可以在同一個聲明中混合實用*和&,如下:

  1. int **p1; // p1 is a pointer to a pointer to an int. int *&p2; // p2 is a reference to a pointer to an int. int &*p3; // ERROR: Pointer to a reference is illegal. int &&p4;// ERROR: Reference to a reference is illegal. 

注:p1是一個int類型的指針的指針;p2是一個int類型的指針的引用;p3是一個int類型引用的指針(不合法!);p4是一個int類型引用的引用(不合法!)。

const修飾符

當你想阻止一個變量被改變,可能會用到const關鍵字。在你給一個變量加上const修飾符的同時,通常需要對它進行初始化,因為以後的任何時候你將沒有機會再去改變它。例如:

  1. const int n=5; int const m=10; 

上述兩個變量n和m其實是同一種類型的——都是const int(整形恆量)。因為C++標准規定,const關鍵字放在類型或變量名之前等價的。我個人更喜歡第一種聲明方式,因為它更突出了const修飾符的作用。當const與指針一起使用時,容易讓人感到迷惑。例如,我們來看一下下面的p和q的聲明:

  1. const int *p; int const *q; 

他們當中哪一個代表const int類型的指針(const直接修飾int),哪一個代表int類型的const指針(const直接修飾指針)?實際上,p和q都被聲明為const int類型的指針。而int類型的const指針應該這樣聲明:

  1. int * const r= &n;// n has been declared as an int 

這裡,p和q都是指向const int類型的指針,也就是說,你在以後的程序裡不能改變*p的值。而r是一個const指針,它在聲明的時候被初始化指向變量n(即r=&n;)之後,r的值將不再允許被改變(但*r的值可以改變)。

組合上述兩種const修飾的情況,我們來聲明一個指向const int類型的const指針,如下:

  1. const int * const p=&n // n has been declared as const int 

下面給出的一些關於const的聲明,將幫助你徹底理清const的用法。不過請注意,下面的一些聲明是不能被編譯通過的,因為他們需要在聲明的同時進行初始化。為了簡潔起見,我忽略了初始化部分;因為加入初始化代碼的話,下面每個聲明都將增加兩行代碼。

  1. char ** p1; // pointer to pointer to char const char **p2;// pointer to pointer to const char char * const * p3;// pointer to const pointer to char const char * const * p4;// pointer to const pointer to const char char ** const p5;// const pointer to pointer to char const char ** const p6;// const pointer to pointer to const char char * const * const p7;// const pointer to const pointer to char const char * const * const p8;// const pointer to const pointer to const char 

注:p1是指向char類型的指針的指針;p2是指向const char類型的指針的指針;p3是指向char類型的const指針;p4是指向const char類型的const指針;p5是指向char類型的指針的const指針;p6是指向const char類型的指針的const指針;p7是指向char類型const指針的const指針;p8是指向const char類型的const指針的const指針。

typedef的妙用

typedef給你一種方式來克服“*只適合於變量而不適合於類型”的弊端。你可以如下使用typedef:

  1. typedef char * PCHAR; PCHAR p,q; 

這裡的p和q都被聲明為指針。(如果不使用typedef,q將被聲明為一個char變量,這跟我們的第一眼感覺不太一致!)下面有一些使用typedef的聲明,並且給出了解釋:

  1. typedef char * a;// a is a pointer to a char  
  2. typedef a b();// b is a function that returns // a pointer to a char  
  3. typedef b *c;// c is a pointer to a function // that returns a pointer to a char  
  4. typedef c d();// d is a function returning // a pointer to a function // that returns a pointer to a char  
  5. typedef d *e;// e is a pointer to a function // returning a pointer to a // function that returns a // pointer to a char e var[10];// var is an array of 10 pointers to // functions returning pointers to // functions returning pointers to chars. 

typedef經常用在一個結構聲明之前,如下。這樣,當創建結構變量的時候,允許你不使用關鍵字struct(在C中,創建結構變量時要求使用struct關鍵字,如struct tagPOINT a;而在C++中,struct可以忽略,如tagPOINT b)。

  1. typedef struct tagPOINT { int x; int y; }POINT; POINT p; /* Valid C code */ 

函數指針

函數指針可能是最容易引起理解上的困惑的聲明。函數指針在DOS時代寫TSR程序時用得最多;在Win32和X-Windows時代,他們被用在需要回調函數的場合。當然,還有其它很多地方需要用到函數指針:虛函數表,STL中的一些模板,Win NT/2K/XP系統服務等。讓我們來看一個函數指針的簡單例子:

  1. int (*p)(char); 

這裡p被聲明為一個函數指針,這個函數帶一個char類型的參數,並且有一個int類型的返回值。另外,帶有兩個float類型參數、返回值是char類型的指針的指針的函數指針可以聲明如下:

  1. char ** (*p)(float, float); 

那麼,帶兩個char類型的const指針參數、無返回值的函數指針又該如何聲明呢?參考如下:

  1. void * (*a[5])(char * const, char * const); 

右左法則

“右左法則”是一個簡單的法則,但能讓你准確理解所有的聲明。這個法則運用如下:從最內部的括號開始閱讀聲明,向右看,然後向左看。當你碰到一個括號時就調轉閱讀的方向。括號內的所有內容都分析完畢就跳出括號的范圍。這樣繼續,直到整個聲明都被分析完畢。

對上述“右左法則”做一個小小的修正:當你第一次開始閱讀聲明的時候,你必須從變量名開始,而不是從最內部的括號。

下面結合例子來演示一下“右左法則”的使用。

  1. int * (* (*fp1) (int) ) [10]; 

閱讀步驟:

1. 從變量名開始——fp1

2. 往右看,什麼也沒有,碰到了),因此往左看,碰到一個*——一個指針

3. 跳出括號,碰到了(int)——一個帶一個int參數的函數

4. 向左看,發現一個*——(函數)返回一個指針

5. 跳出括號,向右看,碰到[10]——一個10元素的數組

6. 向左看,發現一個*——指針

7. 向左看,發現int——int類型

總結:fp1被聲明成為一個函數的指針,該函數返回指向指針數組的指針.

再來看一個例子:

int *( *( *arr[5])())();

閱讀步驟:

1. 從變量名開始——arr

2. 往右看,發現是一個數組——一個5元素的數組

3. 向左看,發現一個*——指針

4. 跳出括號,向右看,發現()——不帶參數的函數

5. 向左看,碰到*——(函數)返回一個指針

6. 跳出括號,向右發現()——不帶參數的函數

7. 向左,發現*——(函數)返回一個指針

8. 繼續向左,發現int——int類型

C++變量聲明相關概念解析

C/C++程序員面試指南

初次接觸C/C++語言注意事項

對C/C++頭文件說明講解

C++聲明語法方法解讀

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