C++typedef的詳細用法

轉自知乎的一段解釋:ios

做者:知乎用戶
連接:https://www.zhihu.com/question/29798061/answer/144423125
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

不太想談#define, 在題主的例子的這種用法裏, 它就是個文本替換工具, 預處理器完成的, 無腦替換, 跟word裏的replace如出一轍, 不關編譯器的事. 我想談一下typedef.c++

搞懂了c++創始人寫的<the design and evolution of cpp>中的下面這個例子, 有助於你理解typdef
typedef int P();
typedef int Q();
class X{
  static P(); //等價於static int P();
  static Q();
};

 

 
這是一個極好的例子, 先問一下 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;

 

3. 舉例
先從初級的開始:數組

整形
typedef int NUM;

 

結構體
typedef struct{int a;} STRTCT;

 

指針
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");
}

 

理解了上面的再看下面這段:

理解複雜的定義和聲明:

在閱讀Linux的內核代碼是常常會遇到一些複雜的聲明和定義,例如:工具

        (1)  void * (* (*fp1) (int)) [10];this

        (2)  float (* (*fp2) (int, int, float)) (int);spa

        (3)  typedef double (* (* (*fp3) ()) [10]) ();指針

               fp3 a;code

        (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指向指針p

 4. 定義一個包含10個整型數的數組

            int arr[10];

 5. 定義一個指向包含10個整型數數組的指針

            int (*pArr) [10];

        用幾行代碼將四、5兩個定義串起來:

int arr[10];
int (*pArr) [10];
pArr = &arr;

6. 定義一個指向函數的指針,被指向的函數有一個整型參數並返回整型值

              int (*pfunc) (int);

 7. 定義一個包含10個指針的數組,其中包含的指針指向函數,這些函數有一個整型參數並返回整型值

             int (*arr[10]) (int);

         用幾行代碼將六、7兩個定義串起來:

int (*pfunc) (int);
int (*arr[10]) (int);
arr[0] = pfunc;

到這一步,彷佛就不是那麼好理解了。如今須要請出用於理解複雜定義的「右左法則」: 

        從變量名看起,先往右,再往左,碰到圓括號就調轉閱讀的方向;括號內分析完就跳出括號,仍是先右後左的順序。如此循環,直到分析完整個定義。

        讓咱們用這個方法來分析上面的第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
相關文章
相關標籤/搜索