如何理解複雜的C/C++ 聲明

 

原作者姓名 Vikram A Punathambekar
文章原始出處 http://www.codeproject.com/cpp/complex_declarations.asp

 

曾經碰到過讓你迷惑不解、類似於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類型引用的引用(不合法!)。

const修飾符

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

 

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

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

 

const int *p; int const *q;

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

 

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指針,如下:

 

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

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

 

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:

 

typedef char * PCHAR; PCHAR p,q;

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

 

typedef char * a;// a is a pointer to a char typedef a b(); // b is a function that returns // a pointer to a char typedef b *c; // c is a pointer to a function // that returns a pointer to a char typedef c d();// d is a function returning // a pointer to a function // that returns a pointer to a char 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)。

 

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


曾經碰到過讓你迷惑不解、類似於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類型引用的引用(不合法!)。

const修飾符

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

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

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

const int *p;
            int const *q;

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

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指針,如下:

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

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

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:

typedef char * PCHAR;
            PCHAR p,q;

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

typedef char * a;
            // a is a pointer to a char
            typedef a b();
            // b is a function that returns
            // a pointer to a char
            typedef b *c;
            // c is a pointer to a function
            // that returns a pointer to a char
            typedef c d();
            // d is a function returning
            // a pointer to a function
            // that returns a pointer to a char
            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)。

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章