C專家編程精華篇----頭疼的C語言聲明:const、typedef、define及函數高級聲明

**************const有關的變量聲明*****************

(1)const == read-only

const修飾的變量被許多人錯誤的認爲是常量,但是const所修飾的變量應該是隻讀變量

檢驗這個結論可以用下面這個方法:

const int two = 2;
switch(i)
{
case 1: printf("case 1.\n");
case two:printf("case 2.\n");
case 3: printf("case 3.\n");
default:
	break;
}

這段代碼會產生一個編譯時的錯誤,switch的i必須用常量或常量表達式,這裏在用two

代替的時候出現error,很明顯,two並不是常量,它是一個只讀變量,實質還是變量。

(有人提到這段代碼在微軟的vc6.0和vs2010下能編譯通過並且正確運行,一開始我是這麼認爲的:

這個其實和編譯器對代碼的處理有關,因爲在gcc下這段代碼會提示如下問題:

error:case label does not reduce to an integer constant

warning:variable [two] set but not used

後來在衆多牛人的解釋之下,我才發現,原來這不是編譯器的問題,而只是const在C和C++中意義不一樣

C++中將const定義爲了常量。

(2)const修飾指針

看這四個聲明:

int const *i;
const int *i;
int * const i;
const int * const i;

到底const的修飾對指針i起只讀作用,還是對i所指向的int值起只讀作用呢?

這裏有一種直觀的做法來認清它的真面目:

int const *i; const int *i;int * const i;constint * const i;

結果是不是一目瞭然?將數據類型直接劃掉即可

最後一個聲明保證了指針i和i指向的對象都爲只讀變量。

const最有用之處就是用它來限定函數的形參,比如strcpy函數的原型。

**************typedef與define的區別*****************

(1)整個結構體的定義提倡這樣定義:

struct student
{
	int id;
	char name[10];
};
struct student LiSi,ZhangSan;
這種定義方式才易於閱讀,雖然多寫了一點代碼

(2)糾結的結構標籤和結構類型

typedef struct foo{int i;}foo;
struct foo{int i;}foo;
我們如果用sizeof(foo)的話,編譯器是不是肯定會報錯的?這當時肯定是的。
第一個定義中,聲明瞭結構標籤foo(第一個)和有typedef聲明的結構類型foo(第二個)。

使用結構標籤的foo效果:struct foo value

使用結構類型的foo效果:foo value

第二個定義中,聲明瞭結構標籤foo和變量foo

只有結構標籤能夠在以後的聲明中使用:struct foo value

這就是利用typedef來定義類型的新名字帶來的頭疼之事,所以建議

不要用typedef來做這種複雜的事,下面來看看它與define的區別

(3)typedef與define

typedef等價於一種徹底的“封裝”:聲明之後不能再次增加其他內容

區別一:

#define elem int
unsigned elem i;/* OK */
typedef int elem;
unsigned elem i;/* ERROR */
對宏類型名我們可以用其它類型說明符對其進行擴展,但對typedef所定義的類型名卻不能。

所以第一個定義是對的,但第二個定義會報錯

區別二:

#define int_ptr int *
int_ptr a,b;
typedef int * int_ptr;
int_ptr a,b;
第一個利用宏定義定義a,b變量之後,經過宏擴展a的類型是int *,b的類型卻還是int

第二個typedef之後的a,b變量的類型都是int *,這就是兩者的第二個區別

最後提醒一點,這兩個東西不可以相互嵌套。。。。

(今天剛寫個二叉樹的程序,想用非遞歸實現,需要用到棧,結果把棧的宏定義內容改爲

二叉樹的typedef定義的結構類型就發生ERROR,弄了好久才解決)
(4)typedef的適用

1、數組、結構、指針已經函數的組合類型

2、可移植類型(將數據類型改一下就可以輕鬆適應不同的平臺了)

3、也可以用在強制類型轉換時提供一個簡單的名字

**************各種括號組成的複雜聲明*****************

先來頭腦風暴一下:

int *(*abc)[6];
int *(*abc())();
int *(*(*abc)())[6];
int *(*(*(*abc()))[6])();
大概看到的人都有一點昏迷了。。。。

下面給出讀懂複雜的函數聲明的要訣:

最重要的是弄懂各種操作符的優先級,其次先通過最後一對操作符來判斷是函數還是數組

在函數的高級聲明中,主要用到()、[]和*操作符,只需記住()[]->.四個操作符的優先級最高即可

以第三個爲例,來分析一下高級聲明該怎麼理解:

1、有三對圓括號,最後一對是方括號,說明是一個指向數組的指針吧,數組長度爲6;

2、(這裏有重大修改,先前寫的是從最裏層開始分析)先從最外層開始分析,int *(A)[6],我們

可以確定是一個A是一個指向數據爲int *型數組的指針,數組長度爲6,再分析A

3、A爲(*(*abc)()),從裏層的兩個圓括號看,其中一個圓括號爲空,說明肯定是一個函數,也就是

說整個的最外層int *()[6]肯定是一個函數的返回值了,在看(*abc)這是一個abc指向的函數指針,返

回值是什麼?通過兩個並排的原括號前面的*決定,這裏(*abc)之所以沒有先於*一起結合看而先於()

一起結合看作一個函數,是因爲*的優先級沒()高

4、*決定了裏層的函數指針的返回值,它的內容當然就是外層的那些個東東,即是:指向int型數組

的一個指針

5、先在加上對裏層的分析,結論就是:

返回值爲“指向int型數組的指針”的函數指針

整個的順序是簡化出來是這樣:

int *( )[6]---->確定這是一個指向int型數組的指針

int *( (*abc) )[6]---->abc肯定是一個指針了,具體還無法判斷

int *( (*abc)())[6]---->這下可以確定另一個東西了,abc是一個指針函數,函數總得有返回值吧,繼續

int *(*(*abc)())[6]---->返回值出來了,通過第二個*確定的,它表明返回的是外面那個*所指向的內容

整個過程總結爲:

A、先取最外層的,抽出裏層內容,對裏層的從它的名字開始讀取,按照優先級順序依次讀取

B、優先級順序:

1、聲明中被圓括號括起來的部分

2、後綴操作符:()函數、[]數組

3、前綴操作符:*指向什麼的指針

C、如果還有const、volatile關鍵字在類型說明符前面說明它作用於類型說明符,如前面講的const的

修飾作用,其他情況他倆一般作用於它左邊緊挨的*操作符

很明顯,如果用typedef來爲這些個圓括號重新命名的話,再複雜的函數聲明你剖析的時候都會遊刃有餘的

(由於昨天剛開始寫這個聲明的分析時頭腦還沒怎麼理順這個思路,造成解說的很混亂,今天重新對

此進行整理,如果有錯,請大家友情指出。)

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