close

公證服務推薦

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;
  • 第1行的 typedefunsigned 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;
    
    這個 typedef 寫法用了一個一般比力不常用的 翻譯社. 其實就是和 int32_t a, *p;(註三) 界說了 "一個 32 位元整數變數 a 和一個指到 32 位元整數的指標變數 p" 一樣翻譯社 這樣的寫法界說了一個佈局 _list_node_ 的別號 LIST_NODE, 和一個指標型的別名 pLIST_NODE. 所以本例和上例這二種寫法界說出來的變數 node0, free_list 結果是完全一致的.
  • 註三: 利用 , 將多個變數的定義/宣佈連結起來時, 要注意 * (指標) 其實不算在共同的資料型態這一邊翻譯社 而是算在變數名稱這一邊. 所以上面的例子裡的 int32_t a翻譯社 *p;int32_t *p, a;int32_t* p, a; 意義上都是一樣的. 初學者需要特殊謹慎最後一種寫法, 異常容易讓人弄錯搞含混了.


    註二: ANSI C 標準文件說: 會實際佔據記憶體空間的宣佈稱為定義. 所以 ANSI C 說的宣佈包括了定義純宣佈. 而註一及以下本文中所指的宣告則是指沒有佔據記憶體空間的純宣佈翻譯社 而不是 ANSI C 原先所指的宣佈翻譯社 特此說明. 請參考維基網站 Declaration (computer programming) 段落二 'Declaration vs. definition' 及段落三 'Declarations and Definitions')

    再來看一些用 typedef 轉換化簡的例子: from blog.sina.com.cn typedef的四个用處和两大圈套
    覺得很費勁看不下去了嗎? 先讀一下這一篇 C 說話:輕鬆讀懂複雜的界說 (Define and Read the complex declarations)

    // 原始寫法:
    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

    例二: 變數為一陣列翻譯社 陣列元素內容為 函數指標, 函數之參數為 函數指標

    底下的程式片斷是變數界說的部份, 沒有利用 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 C 說話 typedef function pointer struct pointer pointers 指標 結構指標 函數指標
    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];

    圈套 -- 有關 storage class 和 qualifier 常常呈現的毛病


    1. 由於 typedef功效會被視為資料型態的擴充, 界說或宣佈變數時可以使用指定儲區種別 (storage class) 的四個 keyword (auto, static翻譯社 extern, register) 來加以潤飾, 是以 typedef 的內容本身是弗成以利用這四個 keyword 的.
    2. 轉換化簡的原則:

      • 不克不及損壞原有之運算先後次序.
      • 轉換化簡沒有固定的答案, 完全視程式的需要取 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. 所以多加了括號其實不會改變它的意義.

      下面的例子含有例舉 (enum) 別號界說翻譯社 應當不消多作註釋.

    參考


    1. en.wikipedia.org typedef
    2. blog.sina.com.cn typedef的四个用途和两大圈套
    3. pixnet.net/blog C 說話:輕鬆讀懂複雜的定義 (Define and Read the complex declarations)
    4. Declaring, Assigning, and Using Function Pointers

    下面的例子借用自 Wiki 網站對 typedef 的解說

    // Wrong definition:
    typedef char * pstr;
    mystrcmp(const pstr, const pstr);
    
    // Correct definition:
    typedef const char * cpstr;
    mystrcmp(cpstr, cpstr);
    

enum color { black, white, gold, pink };
typedef enum color iPhoneColor;
iPhoneColor x = gold;

再下來是陣列 array 的例子:

// 原始寫法:
doube(*)() (*e)[9];
// 轉換為:
typedef double (*pFuny)();         // 左半部
typedef pFuny (*pFunParamy)[9];    // 右半部
pFunParamy e;

函數指標最多見的利用是利用在 callback 的手藝上. 由於需要將某一函數的位址當做參數傳送給另一個函數翻譯社 因此使用 typedef 替這種 callback 函數的指標界說一個新名字 (新資料型態), 可以大幅提昇程式的可讀性翻譯社 往後保護及點竄上對照不會出錯.

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 的參數部分, 也變得簡單易讀.

// 原始寫法:
void (*b[10])(void (*)());
// 轉換為:
typedef void (*pFunParam)();       // 右半部, 函數的參數
typedef void (*pFunx)(pFunParam);  // 左半部的函數
pFunx b[10];

例三: 變數為指向陣列之指標, 陣列元素固定為 9, 陣列元素內容為 函數指標

unsigned char  flag1, flag2;
struct _list_node_ {
    unsigned long       size;
    strcut _list_node_ *next;
} node0, *free_list;