下面的例子借用自 Wiki 網站對 typedef 的講解
接著, 我們進一步加一些轉變
// Examples of typedef a pointer typedef struct _list_node_ * pLIST_NODE; // (1) typedef struct _list_node_ (* pLIST_NODE); // (2) // Examples of typedef a function or pointer of function typedef int IamFunc (int, int); // (3) typedef int *IamFunc (int, int); // (4) typedef int (*IamFunc)(int, int); // (5)
- 式子(1) 是利用告終構指標, 寫法很平居, 看起來應該很習慣.
- 式子(2) 的寫法看起來感覺彷佛有點玄機... 但其實並沒有翻譯社 式子(1)和(2)這二個寫法是一樣的:
在界說或宣佈指標 int * ptr 的寫法中翻譯社 星號閣下雙方的空白是可有可無的, 所以 int* ptr, int *ptr翻譯社 int * ptr, int*ptr, 都是准確的並且意義也不異.
而解釋上你可以說 ptr 是一個 int*, 也能夠說 *ptr 是一個 int. 所以多加了括號並不會改變它的意義.
圈套 -- 有關 storage class 和 qualifier 經常泛起的毛病
- 由於 typedef 的成果會被視為資料型態的擴充, 定義或宣佈變數時可使用指定儲區種別 (storage class) 的四個 keyword (auto, static, extern, register) 來加以修飾, 是以 typedef 的內容自己是不可以利用這四個 keyword 的.
- 第1行的 typedef 為 unsigned char 取了個好記的別號 bool.
- 第2~5行則將佈局 _list_node_ 擴充為資料型態並為其定名為 LIST_NODE. 這裡要注意的是構造內部有一個指標指向和本身一樣的構造. 在 typedef 的定義中我們只能利用 struct _list_node_ * 而不可以使用 typedef 的功效 LIST_NODE (因為 LIST_NODE 尚未界說完成.
翻譯公司也能夠把 typedef 的定義和佈局的界說拆開來). - 第7~8行則拿新界說的別號, 來界說原本程式要定義的變數. 假如再把 1~5 行的 typedef 移到標頭檔 (xxx.h)翻譯社 只留下 7~8 行這二行變數界說的部分, 程式看起來就簡練多了.
- 上面有關 LIST_NODE 的部分翻譯社 也可以換一個寫法:
typedef struct _list_node_ { unsigned long size; strcut _list_node_ *next; } LIST_NODE, *pLIST_NODE; LIST_NODE node0; pLIST_NODE free_list;
- en.wikipedia.org typedef
- blog.sina.com.cn typedef的四个用途和两大陷阱
- pixnet.net/blog C 語言:輕鬆讀懂複雜的界說 (Define and Read the complex declarations)
- Declaring翻譯社 Assigning, and Using Function Pointers
- 先取一個適合的截斷點
- 將截斷點以後的低優先權運算以 typedef 界說為別號
- 然後用別名界說或宣告截斷點之前的高優先權運算.
typedef int (*MathFunc)(float, int); int do_math(float arg1, int arg2) { return arg2; } int call_a_func(MathFunc call_this) { int output = call_this(5.5, 7); return output; } int final_result = call_a_func(&do_math);
再來一個更極端的例子 (也是借用自 Wiki 網站對 typedef 的說明註解)
typedef unsigned char bool; typedef struct _list_node_ { unsigned long size; strcut _list_node_ *next; } LIST_NODE; bool flag1翻譯社 flag2; LIST_NODE node0, *free_list;
註三: 使用 翻譯社 將多個變數的定義/宣佈保持起來時翻譯社 要注意 * (指標) 其實不算在配合的資料型態這一邊翻譯社 而是算在變數名稱這一邊. 所以上面的例子裡的 int32_t a, *p; 和 int32_t *p, a; 以及 int32_t* p, a; 意義上都是一樣的. 初學者需要特別小心最後一種寫法, 很是輕易讓人弄錯搞含混了.
參考
底下的程式片段是變數界說的部分, 沒有利用 typedef 的模樣:
// 原始寫法: doube(*)() (*e)[9]; // 轉換為: typedef double (*pFuny)(); // 左半部 typedef pFuny (*pFunParamy)[9]; // 右半部 pFunParamy e;
函數指標最多見的利用是使用在 callback 的手藝上. 由於需要將某一函數的位址當成參數傳送給另外一個函數, 因此使用 typedef 替這類 callback 函數的指標界說一個新名字 (新資料型態), 可以大幅提昇程式的可讀性翻譯社 日後保護及點竄上比力不會犯錯.
// Wrong definition: typedef char * pstr; mystrcmp(const pstr, const pstr); // Correct definition: typedef const char * cpstr; mystrcmp(cpstr翻譯社 cpstr);
轉換化簡程序:
下面的例子含有例舉 (enum) 別名界說翻譯社 應該不用多作诠釋.
轉換化簡
// 原始寫法: void (*b[10])(void (*)()); // 轉換為: typedef void (*pFunParam)(); // 右半部, 函數的參數 typedef void (*pFunx)(pFunParam); // 左半部的函數 pFunx b[10];
例三: 變數為指向陣列之指標, 陣列元素固定為 9翻譯社 陣列元素內容為 函數指標
註二: ANSI C 標準文件說: 會現實佔據記憶體空間的宣佈稱為界說. 所以 ANSI C 說的宣佈包含了界說及純宣告. 而註一及以下本文中所指的宣告則是指沒有佔據記憶體空間的純宣佈, 而不是 ANSI C 本來所指的宣告翻譯社 特此申明. 請參考維基網站 Declaration (computer programming) 段落二 'Declaration vs. definition' 及段落三 'Declarations and Definitions')
註一: 宣佈和定義有所不同. 界說變數會實際佔據記憶體空間, 而宣佈變數則只產生參考的連結, 稍後貫穿連接程式時再連結到在其他模組界說的變數. 我們一般把宣告變數擺放在 header file (.h 檔) 中, 有需要的模組或程式只要 include 便可. 而界說變數則視環境放在主程式或相幹的模組中, 固然它凡是也會 include 該 header file.
C 說話中 typedef 可以用來擴充 C 原本的資料型態. 通常天成翻譯公司們會將某個資料型態或將常用的資料型態組合給予一個對照直觀而易懂的別名. 界說別號之後天成翻譯公司們就能夠像利用原本的資料型態來宣佈或界說變數一樣翻譯社 直接拿它來宣佈或界說(註一翻譯社 註二)變數.
typedef uint8_t Buffer[16]; Buffer xBuf; xBuf[0] = 3; xBuf[1] = 2;
- 第3行有些小小的奇異, 界說變數時彷佛沒有指定是陣列, 後面卻可以用陣列的寫法. 其實第3行相當於 uint8_t xBuf[16]
- 用法可能看起來有點希奇翻譯社 卻可以包管每次用 Buffer 界說或宣佈的陣列變數 一定是 16 個 uint8_t 元素. 好處是陣列的大小需要改變時, 只要點竄 typedef 沒必要全部專案翻找一遍, 還要憂慮是不是有改漏了.
如果碰到看不懂時, 建議你可以把界說中的 typedef 拿掉翻譯社 同時資料型態名稱換成變數的名稱, 就會比力容易理解. 例如: 把 typedef uint8_t Buffer[16]; 去掉 typedef翻譯社 Buffer 換成變數名 xBuf翻譯社 釀成 uint8_t xBuf[16];
void (*signal(int sig, void (*func)(int)))(int); // 轉換成下面的樣子 typedef void (*sighandler_t)(int); sighandler_t signal(int sig翻譯社 sighandler_t func);
// 下行的語法是毛病: static 不可以泛起在 typedef 中 typedef static int newINT; newINT x, y翻譯社 x; // 要改成下列二行才行 (static 必需移到變數界說式) typedef int newINT; static newINT x翻譯社 y, z;
- 下面的式子(1), 式子(2)和式子(3)寫法相通, 是界說一個指標變數指向常數資料 (指標值可變翻譯社 資料值弗成變).
- 式子(4)和式子(5)寫法相通, 是界說一個常數指標指向可變更的資料 (指標值弗成變, 資料值可變).
- 式子(1)的寫法常常會被誤以為應當和式子(5)相等, 所以就毛病的把勢子(1)化簡為式子(5). 但現實上是式子(1)應該以化簡為式子(3). (我們應該把勢子(5) const ptr p 中的 ptr 算作和 const int x 中的 int 一樣, 是一個資料型態. 而不要把它以 typedef 的定義 char * 來替換.)
// define a non-const pointer to const data const char * p; // (1) char const * p; // (2) typedef const char * ptr; // (3-1) ptr p; // (3-2) // define a const pointer to non-const data char * const p; // (4) typedef char* ptr; // (5-1) const ptr p; // (5-2)
- 備註:
- 式子(4)和式子(5-2)在現實應用中是不 OK 的翻譯社 因為 (指標變數的) 變數值本身是一常數, 必須在界說變數的同時指定其常數值. 實際應用的例子以下: 式子(4a)是指定某一個變數的位址; 式子(4b)是指定一特定位址.
char * const p = &x; // (4a) char * const p = 0x200000; // (4b)
- 不外式子(4b)的用法會多浪費一個指標變數的空間 (即變數 p 自己). 這是因為 0x200000 自己就是一個 const翻譯社 所以沒必要用變數來貯存它然後又宣佈說該變數是常數不可以更動. 其實天成翻譯公司們可以直接用 type casting 的方式把 0x200000 轉型就可以了翻譯社 即 ((char *)0x200000). 如果感覺後續利用它的程式敘述會不好讀, 那可以到場 #define CONST_P ((char *)0x200000) 這樣的置換巨集, 然後把程式敘述改成利用 CONST_P 來取代 ((char *)0x200000) 即可.
- 式子(1)翻譯社 式子(2)和式子(3)在現實應用中是 OK 的, 同時它只是限定不可以經過指標變數 p 來改變其所指到的變數, 而不是限定所指到的變數必需是常數.
typedef const char * ptr; ptr p; char x = 0x20; p = &x; *p = 0x21; // Compiler will alert. x = 0x21; // OK
最常看到的毛病類型是我們想要寫一個像 strcmp() 那樣的函數, 於是宣佈了以下的函數原型 mystrcmp(const char *翻譯社 const char *), 然後為了想簡化於是又增添了定義 typedef char * pstr; 接著把函數原型宣告改成 mystrcmp(const pstr, const pstr), 然後就掛掉了... (我們但願的是字串對照時不要去動到字串的內容, 而不是指標值不克不及更動)
// 原始寫法: int *(*a[5])(int翻譯社 char*); // 轉換1: typedef int *(*pFun)(int, char*); pFun a[5]; // 轉換2: typedef int *Func(int, char*); Func *a[5];
- 轉換1: 在 *a[5] 中, [] 優先權比 * 高, 故把 a[5] 留在本來變數界說的式子翻譯社 其餘的轉為 typedef
- 轉換2: 把 *a[5] 全部留在本來的變數界說式, 其餘的轉為 typedef
例二: 變數為一陣列, 陣列元素內容為 函數指標, 函數之參數為 函數指標
- 式子(3) 實際是界說一個體名 IamFunc, 它是一個傳回值為整數且需要二個整數參數的函數.
- 式子(4) 和式子(3) 對照翻譯社 只是回傳值由 int 變為 int*. 因為在 IamFunc 右邊的函數呼叫運算子 (), 比在左側的取值運算子 *, 優先官僚來得較高.
- 式子(5) 和式子(3) 比較, 多加了括號強迫取值運算子 * 先執行, 所以函數釀成了指向函數的指標, (名為 IamFunc 的資料型態, 是一個指標指向一個傳回值為整數且需要二個整數參數的函數).
- 有人說 式子(3) 和式子(5) 是對等的, 關於這點我不是很清晰翻譯社 需要多一點時間找資料及作些實行來驗證.
- 謎底也對也錯: 就 typedef 的界說內容來講: 他們是紛歧樣的. 然則就後續程式的現實利用來說: 這二個 typedef 界說完全一致翻譯社 程式的寫法不異, 效果也完全一樣. 之所以會有這類景象, 是因為 C 編譯器對 function pointer 的定義和處置懲罰體式格局和其他指標其實不一樣翻譯社 並且還有點另類: function pointer 的取址運算 & 及取值運算 * 成績或有不同 (一個是指標的位址, 一個是函數的位址) 可是只要一帶上 () 成績都是呼喚函數並且不會搞錯 (好奇異喔!). 這裡有一篇有關 function pointer 的申明 (英文) Declaring, Assigning翻譯社 and Using Function Pointers 是 Usenet 上 comp.lang.c FAQ list 的維護人 Steve Summit 師長教師寫的, 對於 function pointer 的取址運算 & 及取值運算 * 有明白的解說, 供應給人人參考.
雜記
- 習慣上, C 語言 (如: standard C library, POSIX) 會在衍素性型別名的後面加上 _t, 像是 size_t.
- 界說或宣佈變數時翻譯社 新設的型別不行以和 signed, unsigned 一路適用 (即使是 原始型別是 int翻譯社 short翻譯社 long... 之類的型別). 來由很簡單 signed int 和 unsigned int 是劃分的基本資料型態, 意即 signed 和 unsigned 這二個 keyword 並非 int 的 storage class 或者是 qualifier 之類的修飾 keyword.
enum color { black, white, gold, pink }; typedef enum color iPhoneColor; iPhoneColor x = gold;
再下來是陣列 array 的例子:
例一: 變數為一陣列翻譯社 陣列元素內容為 函數指標
再來看一些用 typedef 轉換化簡的例子: from blog.sina.com.cn typedef的四个用處和两大圈套
覺得很費勁看不下去了嗎? 先讀一下這一篇 C 說話:輕鬆讀懂複雜的界說 (Define and Read the complex declarations)
轉換化簡的原則:
- 不克不及損壞原有之運算先後順序.
- 轉換化簡沒有固定的謎底, 完全視程式的需要取 typedef 的截斷點.
int do_math(float arg1, int arg2) { return arg2; } int call_a_func(int (*call_this)(float翻譯社 int)) { int output = call_this(5.5, 7); return output; } int final_result = call_a_func(&do_math);
套用 typedef 以後翻譯社 typedef 自己易讀而且 call_a_func 的參數部分, 也變得簡單易讀.
unsigned char flag1翻譯社 flag2; struct _list_node_ { unsigned long size; strcut _list_node_ *next; } node0, *free_list;
再來看的是改用 typedef 後的模樣:
文章出自: http://magicjackting.pixnet.net/blog/post/65865174-c-%e8%aa%9e%e8%a8%80%3atypedef-%e7%9a%84%e7%94%a8有關各國語文翻譯公證的問題歡迎諮詢天成翻譯公司02-77260931
留言列表