1. 程式人生 > >C語言基礎(二)之複雜宣告方式

C語言基礎(二)之複雜宣告方式

我們本篇部落格的內容主要是解決如何閱讀C語言的宣告。比如:

  • char a;
  • char * b;
  • const char * c;
  • char * const d;
  • char e[100];
  • char *f[100];
  • char (*g)[100];
  • struct student h;
  • int def1();
  • int * def2();
  • int (*def3)();

以上的宣告形式我想大部分我們都會遇到(實際上我們上面的形式都會用到),針對這些形式,我們會給出解析的方法。

變數的宣告形式主要有兩種解析的方法,一種是定義解析法,一種是形式解析法。
定義解析法就是依據變數宣告的定義方法去解析,比如說int a;我們根據int型別的變數定義形式,我們很容易知道a是一個int型別的變數。這種解析法,簡單直觀,但是對於複雜宣告的解析就比較困難,因為我們不是很容易給出一個簡單的定義形式來表示複雜的宣告。
形式解析法是依據從右向左解析的方式,每次解析只針對特定的部分。這種解析方式需要在原有的形式上做些形式轉化。我門使用符號“{}”來定義解析元素的概念,下面重點介紹這個方法(此方法是我個人的理解,但是,的確很有用,這個方法只是形式上的變化)。

形式: char a;
如果要做形式轉換首先我們要確定那個東西是變數。變數的基本定義中有一條是變數不可能是關鍵字的字串,依據這點我們很容易轉換上述的形式:
char a;—> {char}2 {a}1
解析的方式依據從右向左的方式,
1.{a}1表示這是一個變數,變數名是a。
2.{char}2表示這個變數是一個char型別的變數。
依據上面的解析說明,我們知道這個宣告表示我們定義了一個char型別的變數a。

形式:char * b;
轉換後的形式:{char}3 {*}2 {b}1
解析步驟:
1.{b}1 表示這是變數名為b的變數
2.{*}2 表示這個變數是一個指標型別的變數
3.{char}3 表示這個指標型別的是一個char型別
總結:定義了一個char型別的指標變數b

形式:const char * c;
轉換後的形式:{char}4 {const}3 {*}2 {c}1
解析步驟:
1. {c}1 表示這是一個變數名為c的變數
2. {*}2 表示這個變數是一個指標型別的變數
3. {const}3 表示這個指標型別變數不能作為左值
4. {char}4 表示這個指標型別是一個char型別
總結:定義了一個char型別的指標變數c,該變數的間接引用形式的表示式不能作為左值
說明:
1. const char 和形式char const 是具有同樣的定義
2. const的語義只是是限定定義的變數空間不可被修改,這種語義用另一種方式來說就是,這個變數空間不能做為左值的。上面的形式有個額外的意思指標型別的表示式不能做為左值的, 也就是說*(c+1) = ‘x’;這個表示式不允許的。

形式:char * const d;
轉換後的形式: {char}4 {*}3 {const}2 {d}1
解析步驟:
1. {d}1 表示這是一個變數名為d的變數
2. {const}2 表示這個變數是不能作為左值
3. {*}3 表示這個變數是一個指標型別的變數
4. {char}4 表示這個指標型別是 char 型別
總結:定義了一個char型別的指標變數d, 該變數不能作為左值

形式: char e[100];
轉換後的形式:{char}3 {[100]}2 {e}1
解析步驟:
1. {e}1 表示這是一個變數名為e的變數
2. {[100]}2 表示該變數是一個數組型別的變數,這個陣列大小是100
3. {char}3 表示這個陣列儲存的值是char型別
總結: 定義了儲存char型別的值大小為100的陣列變數e
說明:陣列的解析的方式之所以困難主要是他的轉換形式和我們定義形式是不同的,之所以這樣的轉換,是因為這種形式具有一般性,後面的解析形式會有更進一步的說明

形式: char *f[100];
轉換後的形式: {char}4 {*}3 {[100]}2 {f}1
解析步驟:
1. {f}1 表示這是一個變數名為f的變數
2. {[100]}2 表示這個變數是一個數組型別的變數,陣列的大小是100
3. {*}3 表示這個陣列存的是指標型別的值
4. {char}4 表示這個指標型別是一個char型別
總結:定義了一個儲存char指標型別的值大小為100的陣列變數f
說明:
1. 運算子’[]’ 結合的優先順序比’*’高

形式:char (*g)[100];
轉換後的形式:{char}4 {[100]}3 {*}2 {g}1
解析步驟:
1. {g}1 表示定義了一個變數名為g的變數
2. {*}2 表示這個變數是一個指標型別的變數
3. {[100]}3 表示該指標型別是一個大小為100的陣列型別
4. {char}4 表示該陣列儲存char型別的值
總結:定義一個儲存char型別值的大小為100的陣列型別的指標變數g
說明
1. 如果有’()’這裡面的表示式需要優先解析

形式:struct student h;
轉換後的形式:{struct}3 {student}2 {h}1
解析步驟:
1. {h}1 表示定義了一個變數名為h的變數
2. {student}2 表示定義了自定義型別student的變數
3. {struct}3 表示這個自定義的型別是一個結構體型別
總結:定義了一個結構體為student型別的變數h.
說明 :
1. student是一個自定義的型別,也就說我們必須優先定義好這個型別。C語言的變數型別,主要分成兩大部分,一部分是內建的,另一部分是自定義的,函式型別也是一種自定義的型別。

形式:int def1();
轉換後的形式:{int}3 {()}2 {def1}1
解析步驟:
1. {def1}1 表示定義了一個變數名為def1的變數
2. {()}2 表示該變數是一個函式型別的變數
3. {int}3 表示該函式的返回型別是一個int型別的值
總結:定義了一個返回值為int型別的函式變數def1

形式:int * def2();
轉換後的形式:{int}4 {*}3 {()}2 {def1}1
解析步驟:
1. {def2}1 表示定義了一個變數名為def2的變數
2. {()}2 表示該變數是一個函式型別的變數
3. {*}3 表示該函式的返回值型別是一個指標型別的值
3. {int}4 表示該指標型別的是一個int型別
總結:定義了一個返回值為int型別的指標型別的值的函式變數def2

形式:int (*def3)();
轉換後的形式:{int}4 {()}3 {*}2 {def3}1
解析步驟:
1. {def3}1 表示定義了一個變數名為def3的變數
2. {*}2 表示該變數是一個指標型別的變數
3. {()}3 表示該指標型別是一個函式型別
3. {int}4 表示該函式返回值是一個int型別
總結:定義了一個返回值為int型別的函式型別的指標變數def2

如果善於總結的讀者,可能會從我上面的分析過程中的得到一些規律的東西,個人很慚愧無法說明這種規律性,但是,我感覺這種規律應該是定義C語言的語義的時候就有的,也就說,可以編譯原理的語法解析層面來進行解釋。

作為一個練習,我們來看一個形式(這個形式是真實存在在標準庫中的):
void (*signal(int sig, void(* func)(int))) (int);

這個形式看上去很複雜,因為這含有一個巢狀的型別定義,但是他的轉換形式很簡單:
轉換後的形式:{void}5 {(int)}4 {*}3 {(int sig, void (*func)(int))}2 {signal}1
解析步驟:
1. {signal}1 定義了變數名為signal的變數
2. {(int sig, void (*func)(int))}2 表示該變數是一個函式變數
3. {*}3 表示該函式的返回一個指標型別的值
4. {(int)}4 表示該指標型別是一個函式型別
5. {void}5 表示該函式返回無型別的值
總結:定一個返回無型別的函式的指標型別的函式變數signal。
非常難以說明!!!!