說明:這個C語言專題,是學習iOS開發的前奏。也為了讓有面向對象語言開發經驗的程序員,能夠快速上手C語言。如果你還沒有編程經驗,或者對C語言、iOS開發不感興趣,請忽略
這講介紹C語言中很常用的一個關鍵字---typedef。
一、typedef作用簡介
* 我們可以使用typedef關鍵字為各種數據類型定義一個新名字(別名)。
1 #include <stdio.h> 2 3 typedef int Integer; 4 typedef unsigned int UInterger; 5 6 typedef float Float; 7 8 int main(int argc, const char * argv[]) { 9 Integer i = -10; 10 UInterger ui = 11; 11 12 Float f = 12.39f; 13 14 printf("%d %d %.2f", i, ui, f); 15 16 return 0; 17 }
在第3、第4、第6行分別給int、unsigned int、float起了個別名,然后在main函數中使用別名定義變量,用來跟原來的基本類型是完全一樣的。輸出結果:
當然,給類型起別名后,原來的int、float還是可以正常使用的:
int i = 10; float f = 10.0f;
* 也可以在別名的基礎上再起一個別名
typedef int Integer; typedef Integer MyInteger;
二、typedef與指針
除開可以給基本數據類型起別名,typedef也可以給指針起別名
1 #include <stdio.h> 2 3 typedef char *String; 4 5 int main(int argc, const char * argv[]) { 6 // 相當於char *str = "This is a string!"; 7 String str = "This is a string!"; 8 9 printf("%s", str); 10 11 return 0; 12 }
在第3給指針類型char *起別名為String,然后在第7行使用String定義了一個字符串,是不是有點Java的感覺?
三、typedef與結構體
給結構體起別名可以使代碼更加簡潔明
1.默認情況下結構體變量的使用
1 // 定義一個結構體 2 struct MyPoint { 3 float x; 4 float y; 5 }; 6 7 int main(int argc, const char * argv[]) { 8 // 定義結構體變量 9 struct MyPoint p; 10 p.x = 10.0f; 11 p.y = 20.0f; 12 13 return 0; 14 }
默認情況下,我們定義結構體變量需要帶個struct關鍵字,看第9行
2.使用typedef給結構體起別名
1 // 定義一個結構體 2 struct MyPoint { 3 float x; 4 float y; 5 }; 6 7 // 起別名 8 typedef struct MyPoint Point; 9 10 int main(int argc, const char * argv[]) { 11 // 定義結構體變量 12 Point p; 13 p.x = 10.0f; 14 p.y = 20.0f; 15 16 return 0; 17 }
我們在第8行給結構體MyPoint起了個別名叫做Point,然后在12行使用Point定義了一個結構體變量p,不用再帶上struct關鍵字了
其實第1~第8行的代碼可以簡寫為:
// 定義一個結構體,順便起別名 typedef struct MyPoint { float x; float y; } Point;
甚至可以省略結構體名稱:
typedef struct { float x; float y; } Point;
三、typedef與指向結構體的指針
typedef可以給指針、結構體起別名,當然也可以給指向結構體的指針起別名
1 #include <stdio.h> 2 3 // 定義一個結構體並起別名 4 typedef struct { 5 float x; 6 float y; 7 } Point; 8 9 // 起別名 10 typedef Point *PP; 11 12 int main(int argc, const char * argv[]) { 13 // 定義結構體變量 14 Point point = {10, 20}; 15 16 // 定義指針變量 17 PP p = &point; 18 19 // 利用指針變量訪問結構體成員 20 printf("x=%f,y=%f", p->x, p->y); 21 return 0; 22 }
在第4行定義了一個結構體,順便起了個別名叫Point,第10行為指向結構體的指針定義了別名PP。然后在main函數中使用這2個別名。
輸出結果:
四、typedef與枚舉類型
使用typedef給枚舉類型起別名也可以使代碼簡潔。
1 // 定義枚舉類型 2 enum Season {spring, summer, autumn, winter}; 3 // 給枚舉類型起別名 4 typedef enum Season Season; 5 6 int main(int argc, const char * argv[]) { 7 // 定義枚舉變量 8 Season s = spring; 9 10 return 0; 11 }
在第2行定義了枚舉類型,在第4行起了別名為Season,然后在第8行直接使用別名定義枚舉變量,不用再帶上enum關鍵字了。
第1行~第4行代碼可以簡化為:
// 定義枚舉類型,並且起別名 typedef enum Season {spring, summer, autumn, winter} Season
甚至可以省略枚舉名稱,簡化為:
typedef enum {spring, summer, autumn, winter} Season;
五、typedef與指向函數的指針
1.先來回顧下函數指針的知識
1 #include <stdio.h> 2 3 // 定義一個sum函數,計算a跟b的和 4 int sum(int a, int b) { 5 int c = a + b; 6 printf("%d + %d = %d", a, b, c); 7 return c; 8 } 9 10 int main(int argc, const char * argv[]) { 11 // 定義一個指向sum函數的指針變量p 12 int (*p)(int, int) = sum; 13 14 // 利用指針變量p調用sum函數 15 (*p)(4, 5); 16 17 return 0; 18 }
* 在第4行定義了一個sum函數,第12行定義了一個指向sum函數的指針變量p,可以發現,這個指針變量p的定義比一般的指針變量看來復雜多了,不利於理解。
* 第15行調用了p指向的sum函數,輸出結果:
2.為了簡化代碼和方便理解,我們可以使用typedef給指向函數的指針類型起別名
1 #include <stdio.h> 2 3 // 定義一個sum函數,計算a跟b的和 4 int sum(int a, int b) { 5 int c = a + b; 6 printf("%d + %d = %d", a, b, c); 7 return c; 8 } 9 10 typedef int (*MySum)(int, int); 11 12 int main(int argc, const char * argv[]) { 13 // 定義一個指向sum函數的指針變量p 14 MySum p = sum; 15 16 // 利用指針變量p調用sum函數 17 (*p)(4, 5); 18 19 return 0; 20 }
* 看第10行,意思是:給指向函數的指針類型,起了個別名叫MySum,被指向的函數接收2個int類型的參數,返回值為int類型。
* 在第14行直接用別名MySum定義一個指向sum函數的指針變量p,這樣看起來簡單舒服多了。第17行的函數調用是一樣的。
六、typedef與#define
1.先來看看下面的兩段代碼有什么區別(注意每一段的第1行代碼)
* 第1段
1 typedef char *String; 2 3 int main(int argc, const char * argv[]) { 4 String str = "This is a string!"; 5 return 0; 6 }
* 第2段
1 #define String char * 2 3 int main(int argc, const char * argv[]) { 4 String str = "This is a string!"; 5 return 0; 6 }
上面的兩段代碼只是第1行代碼不一樣,運行的效果都是一樣的:定義了一個字符串"This is a string!"。
但它們的實現方式是不一樣的:
- 第1段代碼是用typedef給char *定義別名為String
- 第2段代碼是用char *代替代碼中的宏名String
只看上面兩段代碼,似乎看不太出typedef和#define的區別。
2.再來看一段代碼
1 typedef char *String1; 2 3 #define String2 char * 4 5 int main(int argc, const char * argv[]) { 6 String1 str1, str2; 7 8 String2 str3, str4; 9 return 0; 10 }
第1行給char *起了個別名String1,第2行定義了宏String2。然后在第6、第8行定義了4個變量。
重點來了,注意:在這種情況下,只有str1、str2、str3才是指向char類型的指針變量,str4只是個char類型的變量。
下面簡單分析一下原因:
* 如果連續聲明兩個int類型的變量,我們可以這樣寫:
int a, b;
上面的代碼相當於:
int a; int b;
* 以此類推
1 typedef char *String1; 2 3 String1 str1, str2;
經過typedef處理后,String1也算是一種數據類型,所以第3行代碼相當於
1 String1 str1; 2 String1 str2;
由於String1就是char *,所以上面的兩行代碼等於
char *str1; char *str2;
* 再看看宏定義的情況
1 #define String2 char * 2 3 String2 str3, str4;
因為宏定義純粹是字符串替換,用char *代替String2,所以第3行代碼相當於
char * str3, str4;
其實也就相當於:
char * str3; char str4;
可以看出,只有str4是基本數據類型,str1、str2、str3都是指針類型。
所以,以后給類型起別名,最好使用typedef,而不是使用#define