日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

【转】如何理解c和c++的复杂类型声明

發布時間:2024/4/17 c/c++ 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】如何理解c和c++的复杂类型声明 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉自:http://blog.chinaunix.net/space.php?uid=22889411&do=blog&id=59667

曾經碰到過讓你迷惑不解、類似于int * (* (*fp1) (int) ) [10];這樣的變量聲明嗎?本文將由易到難,一步一步教會你如何理解這種復雜的C/C++聲明。?
我們將從每天都能碰到的較簡單的聲明入手,然后逐步加入const修飾符和typedef,還有函數指針,最后介紹一個能夠讓你準確地理解任何C/C++聲明的“右左法則”。?
需要強調一下的是,復雜的C/C++聲明并不是好的編程風格;我這里僅僅是教你如何去理解這些聲明。注意:為了保證能夠在同一行上顯示代碼和相關注釋,本文最好在至少1024x768分辨率的顯示器上閱讀。

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

int n;

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

int *p;

這個應該被理解為“declare p as an int *”(p是一個int *型的變量),或者說p是一個指向一個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 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 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 */?

函數指針可能是最容易引起理解上的困惑的聲明。函數指針在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);

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

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

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

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類型

還有更多的例子:

float ( * ( *b()) [] )();?
// b is a function that returns a?
// pointer to an array of pointers??
// to functions returning floats.??
void * ( *c) ( char, int (*)());?
// c is a pointer to a function that takes??
// two parameters:??
// a char and a pointer to a?
// function that takes no??
// parameters and returns??
// an int??
// and returns a pointer to void.??
void ** (*d) (int &, char **(*)(char *, char **));?
// d is a pointer to a function that takes??
// two parameters:??
// a reference to an int and a pointer??
// to a function that takes two parameters:??
// a pointer to a char and a pointer??
// to a pointer to a char??
// and returns a pointer to a pointer??
// to a char??
// and returns a pointer to a pointer to void??
float ( * ( * e[10])?? (int &) ) [5];?
// e is an array of 10 pointers to??
// functions that take a single??
// reference to an int as an argument??
// and return pointers to??
// an array of 5 floats.?

??? 吃過晚飯,聽完歌,稍作休息,下面開始總結《如何理解c和c++的復雜類型聲明》,以實現我在《關于指針的學習》中的承諾。?
首先,讓我們看看指針與const結合的聲明:?
const int *p;????? //p是指向常整形的指針?
int const *p;???? //錯誤?
int * const p;?? //p是指向整形的常指針?
const int * const p;??? //p是指向常整形的常指針?
怎么樣?希望大家不會搞混吧,下面我會告訴大家如何去理解,但是寫到這里,我突然想起const的一些東西,這里先插個小插曲,-_-?
相信大家在學習C++時也見到過用const修飾函數的,如:?
const int Fun();?
int Fun() const;?
最后那個表示Fun函數是常成員函數(c++類中),既Fun函數不能修改類中的成員變量和成員函數,這個在大家學習 MFC中會經常看到的,舉個例子:如HWND GetDlgItem(ID) const;這里就不詳細說下去了,大家知道這么個事就行了。?
回到主題,要想很清楚的理解聲明,?
一、我們先從編譯器中的聲明器(declarator)說起,什么是聲明器?簡單來說,聲明器就是標識符和與它組合一起的任何指針、函數括號、數組下標等(定義請參考《c專家編程》),合法的聲明存在以下的限制:?
1、函數的返回值不能是個函數或數組,所以 fun()()和fun()[]是非法的?
2、數組里不能有函數,所以fun[]()是非法的?
二、優先級規則?
A、聲明從它的名字開始讀取,然后安裝優先級從高到低讀取?
B、優先級高低順序:?
?? B1、聲明中被括號括起來部分?
?? B2、后綴操作符:()表示函數? []表示數組?
?? B3、前綴操作符:*表示指向...的指針?
C、如果const和(或)volatile后面緊跟類型(如int等),則const和(或)volatile修飾類型,否則const和(或)volatile修飾緊跟在他們左邊的*指針。?
根據上面的優先級規則,我們來進行下面的練習:?
char * const * (*p)();?
1、根據A規則,從p入手,得到p是...;?
2、根據B1規則,(*p),得到p是指針,指向...;?
3、根據B2規則,()函數下標優先于*,所以(*p)(),得到p是指針,指向一個函數,該函數返回...?
4、根據C規則知道const是修飾它左邊的*不是右邊,所以右邊的*應該是函數的返回值,故得到p是指針,指向一個函數,該函數返回另外一個指針,該指針指向...?
5、根據C規則知道指針指向一個類型為char的常指針。?
6、綜上述,得到p是指針,指向一個函數,該函數返回另外一個指針,而該指針指向一個類型為char的常指針。?
大功告成!!!!!?
以下給出些練習,鞏固下,答案以后給:?
1、char * (*array[10]) (int **p)?
2、void (*signal(int sig, void(*fun)(int)))(int)?
呵呵,相信大家對上面那些聲明也非常厭惡,那么我們來找解決辦法吧,那就是使用typedef,比如對上面的練習中void (*signal(int sig, void(*fun)(int)))(int),用typedef來使用就是:typedef void (*ptr_to_fun)(int)?
??????????? ptr_to_fun signal(int, ptr_to_fun)?
這樣估計就清楚了吧!?
說到這,有些朋友會說:能不能用#define來解決呢??很好,是可以的啦,但是不好,為什么?很多朋友可能都知道了,因為#define只是簡單的符號替換,不進行類型檢查,另外一些如#define max ((x)>(y)?(x):(y))這些會帶來種種不好,這里就不多說了,反正我除了為常量使用#define外,我都不用#define。?
好了,大家看完后,可以休息,也可以看下上面練習的答案,檢查下,有疑問的請跟帖提出:?
1、array是個數組,該數組的元素是指針,該指針指向一個函數,這個函數的參數是指向指針的指針p,函數返回另外一個指針,而這個指針又指向char類型。?
2、fun是個函數指針,函數的參數是 int,返回值是void,signal是個函數,其中一個參數是int,另外一個參數是fun,返回另一個指向函數的指針,該函數參數為int,返回 void(其實就是fun)

轉載于:https://www.cnblogs.com/bluebean/p/5907218.html

總結

以上是生活随笔為你收集整理的【转】如何理解c和c++的复杂类型声明的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。