typedef的详细用法
不太想談#define, 在題主的例子的這種用法里, 它就是個文本替換工具, 預處理器完成的, 無腦替換, 跟word里的replace一模一樣, 不關編譯器的事. 想談一下typedef.
搞懂了c++創始人寫的<the design and evolution of cpp>中的下面這個例子, 有助于你理解typedef:
typedef int P(); typedef int Q(); class X {static P(Q); // 等價于`static int Q()`, Q在此作用域中不再是一個類型static Q(P); // 等價于`static int Q(int ())`, 定義了一個名為Q的function };這是一個極好的例子, 先問一下 typedef int P()到底做了什么? 其實是:
declares a function type P as returning an int and taking no arguments.1. 官方定義
初次接觸此類typedef用法的程序員直觀上理解這個例子比較困難, 我們來看一下typedef的官方定義:
Typedef does not work like typedef [type] [new name]. The [new name] part does not always come at the end.
You should look at it this way: if [some declaration] declares a variable, typedef [same declaration] would define a type.
看我標黑的這句話, 總結一下就是: 任何聲明變量的語句前面加上typedef之后,原來是變量的都變成一種類型。不管這個聲明中的標識符號出現在中間還是最后.
2. 隱藏技能
typedef 定義的新類型, 使用時可以省略括號.
什么意思???????????
typedef int NUM; NUM a = 10; // 也可寫成`NUM(a) = 10;` NUM(b) = 12; // 也可寫成`NUM b = 12;`3. 舉例
先從初級的開始:
整型:
typedef int x; // 定義了一個名為x的int類型結構體
typedef struct { char c; } s; // 定義名為s的struct類型指針
typedef int *p; //定義了一個名為p的指針類型, 它指向int (中文描述指針好累)接下來是高級的(注意標識符不一定在最后):
數組
typedef int A[]; // 定義一個名為A的ints數組的類型函數
typedef int f(); // 定義一個名為f, 參數為空, 返回值為int的函數類型 typedef int g(int); // 定義一個名為g, 含一個int參數, 返回值為int行的函數類型現在回過頭看:
typedef int P(); static P(Q);應該就比較好理解了, P是一個新定義的function類型, 它返回值為int, 無參數
根據我的第2點說明, P(Q); 實際上等價于P Q, 聲明Q是一個返回值為int, 無參數的函數.
這玩意有什么用呢?我們都知道C++語言里, 函數都是先聲明后使用的(除非在使用之前定義), 看以下例子:
#include <iostream> #include <stdio.h> #include <string>typedef int P(); // 簡單的 typedef void Q(int *p, const std::string& s1, const std::string& s2, size_t size, bool is_true); // 復雜的 class X { public:P(eat_shit); // 等價于聲明`int eat_shit();`Q(bullshit); // 等價于聲明`void bullshit(int *p, const string& s1, const string& s2, size_t size, bool is_true);` };int main() {X *xx;printf("shit ret: %d\n", xx->eat_shit());int a[] = {1, 3, 4, 5, 7};xx->bullshit(a, "foo", "bar", sizeof(a)/sizeof(int), true); }int X::eat_shit() {return 888; }void X::bullshit(int *p, const std::string& s1, const std::string& s2, size_t size, bool is_true) {std::cout << "s1: " << s1 << ", s2: " << s2 << ", size: " << size << std::endl;printf("elems:\n");for(int i = 0; i < size; i++) {printf("%d %s", *p++, (i == size-1) ? "" : ",");}printf("\n"); }創造typedef的原因
在閱讀Linux的內核代碼是經常會遇到一些復雜的聲明和定義,例如:
(1) void * (* (*fp1) (int)) [10];(2) float (* (*fp2) (int, int, float)) (int);(3) typedef double (* (* (*fp3) ()) [10]) ();fp3 a;(4) int (* (*fp4()) [10]) ();剛看到這些聲明或者定義時,一些初學者甚至有一定經驗的工程師都有可能頭皮發毛,基于大惑不解。如果缺乏經驗和方法來對這些內容進行理解,勢必會讓我們浪費大量的時間。
我嘗試對這些內容進行疏理和總結,為自己和有同樣困惑的同學答疑解惑。要理解這些復雜的聲明和定義,我覺得首先不能著急,應該由淺而深,逐步突破。下面先看一些簡單的定義:
1. 定義一個整型數int a;2. 定義一個指向整型數的指針int *p;3. 定義一個指向指針的指針,它指向的指針指向一個整型數int **pp;到這一步我想大多數人都還好理解,我們可以用一些簡單的代碼把這三條給串起來:
int a; int *p; int **pp;p = &a; // p指向整數a所在的地址 pp = &p; // pp指向指針p4.定義一個包含10個整型數的數組
int arr[10];5 定義一個指向包含10個整型數數組的指針
int (*pArr) [10];用幾行代碼將4、5兩個定義串起來:
int arr[10]; int (*pArr) [10];pArr = &arr;6.定義一個指向函數的指針,被指向的函數有一個整型參數并返回整型值
int (*pfunc) (int);7.定義一個包含10個指針的數組,其中包含的指針指向函數,這些函數有一個整型參數并返回整型值
int (*arr[10]) (int);用幾行代碼將6、7兩個定義串起來:
int (*pfunc) (int); int (*arr[10]) (int); arr[0] = pfunc;到這一步,似乎就不是那么好理解了?,F在需要請出用于理解復雜定義的“ 右左法則”:
從變量名看起,先往右,再往左,碰到圓括號就調轉閱讀的方向;括號內分析完就跳出括號,還是先右后左的順序。如此循環,直到分析完整個定義。
讓我們用這個方法來分析上面的第6條定義:
int (*pfunc) (int);找到變量名pfunc,先往右是圓括號,調轉方向,左邊是一個*號,這說明pfunc是一個指針;然后跳出這個圓括號,先看右邊,又遇到圓括號,這說明(*pfunc)是一個函數,所以pfunc是一個指向這類函數的指針,即函數指針,這類函數具有一個int類型的參數,返回值類型是int。
接著分析第7條定義:
int (*arr[10]) (int);找到變量名arr,先往右是[]運算符,說明arr是一個數組;再往左是一個 * 號,說明arr數組的元素是指針(注意:這里的 * 修飾的不是arr,而是arr[10]。原因是[ ]運算符的優先級比 * 要高,arr先與[]結合。);跳出圓括號,先往右又遇到圓括號,說明arr數組的元素是指向函數的指針,它指向的函數有一個int類型的參數,返回值類型是int。
分析完這兩個定義,相信多數人心里面應該有點譜了。可應該還有人會問:怎么判斷定義的是函數指針(定義6),還是數組指針(定義5),或是數組(定義7)?可以抽象出幾個模式:
- type (var)(…); // 變量名var與結合,被圓括號括起來,右邊是參數列表。表明這是函數指針
- type(var)[ ]; //變量名var與結合,被圓括號括起來,右邊是[]運算符。表示這是數組指針
- type(*var[])…;// 變量名var先與[]結合,說明這是一個數組(至于數組包含的是什么,由旁邊的修飾決定)
至此,我們應該有能力分析文章開始列出來了幾條聲明和定義:
(1) void * (* (*fp1) (int)) [10];找到變量名fp1,往右看是圓括號,調轉方向往左看到 * 號,說明fp1是一個指針;跳出內層圓括號,往右看是參數列表,說明fp1是一個函數指針,接著往左看是 * 號,說明指向的函數返回值是指針;再跳出外層圓括號,往右看是[ ]運算符,說明函數返回的是一個數組指針,往左看是void *,說明數組包含的類型是void *。 簡言之,fp1是一個指向函數的指針,該函數接受一個整型參數并返回一個指向含有10個void指針數組的指針。
(2) float (* (*fp2) (int, int, float)) (int);找到變量名fp2,往右看是圓括號,調轉方向往左看到 * 號,說明fp2是一個指針;跳出內層圓括號,往右看是參數列表,說明 fp2 是一個函數指針,接著往左看是 * 號,說明指向的函數返回值是指針;再跳出外層圓括號,往右看還是參數列表,說明返回的指針是一個函數指針,該函數有一個int類型的參數,返回值類型是float。簡言之,fp2是一個指向函數的指針,該函數接受三個參數(int, int和float),且返回一個指向函數的指針,該函數接受一個整型參數并返回一個float。
(3) typedef double (* (* (*fp3) ()) [10]) (); fp3 a;如果創建許多復雜的定義,可以使用typedef。這一條顯示typedef是如何縮短復雜的定義的。
跟前面一樣,先找到變量名fp3(這里fp3其實是新類型名),往右看是圓括號,調轉方向往左是 * ,說明fp3是一個指針;跳出圓括號,往右看是空參數列表,說明fp3是一個函數指針,接著往左是 * 號,說明該函數的返回值是一個指針;跳出第二層圓括號,往右是[ ]運算符,說明函數的返回值是一個數組指針,接著往左是 * 號,說明數組中包含的是指針;跳出第三層圓括號,往右是參數列表,說明數組中包含的是函數指針,這些函數沒有參數,返回值類型是double。簡言之,fp3是一個指向函數的指針,該函數無參數,且返回一個含有10個指向函數指針的數組的指針,這些函數不接受參數且返回double值。
這二行接著說明:a是fp3類型中的一個。
(4) int (* (*fp4()) [10]) ();這里fp4不是變量定義,而是一個函數聲明。
找到變量名fp4,往右是一個無參參數列表,說明fp4是一個函數,接著往左是 * 號,說明函數返回值是一個指針;跳出里層圓括號,往右是[ ]運算符,說明fp4的函數返回值是一個指向數組的指針,往左是 * 號,說明數組中包含的元素是指針;跳出外層圓括號,往右是一個無參參數列表,說明數組中包含的元素是函數指針,這些函數沒有參數,返回值的類型是int。簡言之,fp4是一個返回指針的函數,該指針指向含有10個函數指針的數組,這些函數不接受參數且返回整型值。
用typedef簡化復雜的聲明和定義
以上我們已經看到了不少復雜的聲明和定義,這里再舉一個例子:
int *(*a[10]) (int, char*);用前面的“右左法則”,我們可以很快弄清楚:a是一個包含10個函數指針的數組,這些函數的參數列表是(int, char*),返回值類型是int*。理解已經不成問題,這里的關鍵是如果要定義相同類型的變量b,都得重復書寫:
int *(*b[10]) (int, char*);這里有沒有方便的辦法避免這樣沒有價值的重復?答案就是用typedef來簡化復雜的聲明和定義。
typedef可以給現有的類型起個別名。這里用typedef給以上a、b的類型起個別名:
typedef int *(*A[10]) (int, char*); // 在之前定義的前面加入typedef,然后將變量名a替換成類型名A現在要再定義相同類型的變量c,只需要:
A c;再看一例:
void (*b[10]) (void (*)());先替換右邊括號里面的參數,將void (*)()的類型起個別名pParam:
typedef void (*pParam) ();再替換左邊的變量b,為b的類型起個別名B:
typedef void (*B) (pParam);原聲明的簡化版:
B b[10];總結
以上是生活随笔為你收集整理的typedef的详细用法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c/c++函数指针(Hook前奏1)
- 下一篇: c/c++ typedef定义函数指针