C++/C複雜聲明的解析

來源:互聯網
上載者:User
C++/C複雜聲明的解析

Posted 星期三, 2009/09/09 - 09:57 by easyeagel

摘要:本文是譯者對codeproject上的一篇文章的翻譯,作者把C++/C中複雜聲明的理解過程進行較為合適與詳細的說明。C++/C中的複雜聲明有時有難理解,本文也給出了著名的“左右原則”,當然這個原則更準確說應該依據某種優先順序進行,但是作者給出這個原則已經可以解決幾乎所有的問題了。英文。

目錄 [隱藏]

  1. 介紹
  2. 基礎
    1. const修飾符
    2. typedef的妙用
    3. 函數指標
  3. “右左法則”[重要!!!]
介紹

曾經碰到過讓你迷惑不解、類似於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型變數的指標。我想在這裡展開討論一下:我覺得在聲明一個指標(或引用)類型的變數時,最好將* (或&)寫在緊靠變數之前,而不是緊跟基本類型之後。這樣可以避免一些理解上的誤區,比如: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 */

 

函數指標

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

閱讀步驟:

 

  • 從變數名開始 -------------------------------------------- fp1
  • 往右看,什麼也沒有,碰到了),因此往左看,碰到一個* ------ 一個指標
  • 跳出括弧,碰到了(int) ----------------------------------- 一個帶一個int參數的函數
  • 向左看,發現一個* --------------------------------------- (函數)返回一個指標
  • 跳出括弧,向右看,碰到[10] ------------------------------ 一個10元素的數組
  • 向左看,發現一個* --------------------------------------- 指標
  • 向左看,發現int ----------------------------------------- int類型

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

再來看一個例子:

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

閱讀步驟:

  • 從變數名開始 -------------------------------------------- arr
  • 往右看,發現是一個數組 ---------------------------------- 一個5元素的數組
  • 向左看,發現一個* --------------------------------------- 指標
  • 跳出括弧,向右看,發現() -------------------------------- 不帶參數的函數
  • 向左看,碰到* ------------------------------------------- (函數)返回一個指標
  • 跳出括弧,向右發現() ------------------------------------ 不帶參數的函數
  • 向左,發現* --------------------------------------------- (函數)返回一個指標
  • 繼續向左,發現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.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.