天天看點

c語言之左右法則

曾經碰到過讓你迷惑不解、類似于int * (* (*fp1) (int) ) [10];這樣的變量聲明嗎?本文将由易到難,一步一步教會你如何了解這種複雜的

C/C++聲明:我們将從每天都能碰到的較簡單的聲明入手,然後逐漸加入const修飾符和typedef,還有函數指針,最後介紹一個能夠讓你準确地 了解任何C/C++聲明的“右左法則”。需要強調一下的是,複雜的C/C++聲明并不是好的程式設計風格;我這裡僅僅是教你如何去了解這些聲明。

基礎

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

int n;

這個應該被了解為“declare n as an int”(n是一個int型的變量)。

接下去來看一下指針變量,如下:

int *p;

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

上的誤區,比如:

int* p,q;

第一眼看去,好像是p和q都是int*類型的,但事實上,隻有p是一個指針,而q是一個最簡單的int型變量。

我們還是繼續我們前面的話題,再來看一個指針的指針的例子:

char **argv;

理論上,對于指針的級數沒有限制,你可以定義一個浮點類型變量的指針的指針的指針的指針…

再來看如下的聲明:

int RollNum[30][4];

int (*p)[4]=RollNum;

int *q[5];

這裡,p被聲明為一個指向一個4元素(int類型)數組的指針,而q被聲明為一個包含5個元素(int類型的指針)的數組。

另外,我們還可以在同一個聲明中混合實用*和&,如下:

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類型引用的引 用(不合法!)。

函數指針

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

int (*p)(char);

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

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

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

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

The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.

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

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

一些簡單的例子來說明

1.int a; 整形變量

2.int *a; 整形指針變量

3.int **a; 整形指針的指針變量(一個指向指針的指針,它指向的指針指向一個整形數)

4.int a[10]; 整形數組

5.int *a[10]; 整形指針數組(一個數組中有10個指針,該指針指向一個整形數)

6.int (*a)[10]; 整形數組指針(一個指向有10個整形數組的指針)

7.int (*a)(int); 函數指針變量(一個指向函數的指針,該函數有一個整形參數并傳回一個整形數)

8.int (*a[10])(int); 函數指針數組(一個有10個指針的數組,該指針指向一個函數,該函數有一個整形參數并傳回一個整形數)

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

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

閱讀步驟:

1. 從變量名開始 ——————————————– fp1

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

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

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

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

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

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

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

再來看下面的一些例子:

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

總結: arr是一個函數指針數組,該數組裡的元素指向一個形參為空,傳回值是一個函數指針,該指針指向一個形參為空,傳回值為int*。

float ( * ( *b()) [] )();

總結: b是一個函數,該函數形參為空,傳回值是一個數組指針,該指針指向一個函數指針數組,數組的元素指向一個形參為空,傳回值為float的函數。

void * ( * c) ( char a, int (*b)());

總結: c是一個函數指針變量,指向一個有兩個參數的函數,該函數傳回值為void * 類型,第一個參數為char,第二個形參為函數指針,指針指向一個形參為空,傳回值為int * 的函數。

float ( * ( * e[10]) (int *) ) [5];

總結:e是一個函數指針數組數組裡的元素指向一個形參為int * ,傳回值是一個數組指針,指向的數組為float型的數組。

繼續閱讀