2009-11-04 85 views
591

我是C編程的初學者,但我想知道在定義結構時使用typedef與不使用typedef有何區別。在我看來,實際上沒有什麼區別,他們完成了同樣的任務。typedef結構與結構定義

struct myStruct{ 
    int one; 
    int two; 
}; 

typedef struct{ 
    int one; 
    int two; 
}myStruct; 
+0

我剛剛在這裏閱讀,第二個選項會給編譯器錯誤?! 「傳遞不兼容的指針類型的參數」http://stackoverflow.com/questions/12708897/typedefs-struct-tag-and-alias – hewi 2015-12-18 16:10:08

回答

769

常見成語是同時使用:

typedef struct X { 
    int x; 
} X; 

它們是不同的定義。爲了使討論更加清楚我會分裂了一句:

struct S { 
    int x; 
}; 

typedef struct S S; 

在第一行要定義的結構命名空間中的標識符S(而不是在C++意義上的)。您可以使用它,並通過定義爲struct S參數的類型來定義新定義類型的變量或函數參數:

void f(struct S argument); // struct is required here 

第二行增加了一個類型別名S在全局命名空間,從而允許您只寫:

void f(S argument); // struct keyword no longer needed 

注意,因爲這兩個標識符名稱空間是不同的,定義S無論是在結構和全球空間是不是一個錯誤,因爲它不是重新定義相同的標識符,而是創造了一個不同的標識符一個不同的地方。

賺取差價更清晰:

typedef struct S { 
    int x; 
} T; 

void S() { } // correct 

//void T() {} // error: symbol T already defined as an alias to 'struct S' 

您可以定義與結構的名稱相同的功能的標識都保存在不同的空間,但你不能用相同的名稱定義一個函數當這些標識符發生碰撞時。

在C++中,它稍有不同,因爲定位符號的規則已經微妙地改變了。 C++仍保持着兩個不同的標識符空間,但與C,當你只定義在類標識符空間內的符號,你是不是需要提供結構/ class關鍵字:

// C++ 
struct S { 
    int x; 
}; // S defined as a class 

void f(S a); // correct: struct is optional 

是什麼改變搜索規則,而不是定義標識符的位置。編譯器將搜索全局標識符表,並且在S尚未找到之後,它將在類標識符內搜索S

以同樣的方式的行爲之前給出的代碼:

typedef struct S { 
    int x; 
} T; 

void S() {} // correct [*] 

//void T() {} // error: symbol T already defined as an alias to 'struct S' 

在第二行中的S函數的定義之後,結構s不能是由編譯器自動解決,並創建一個對象或定義類型的參數必須退回到包括struct關鍵字:

// previous code here... 
int main() { 
    S(); 
    struct S s; 
} 
+12

偉大的答案顯示我也爲什麼我想要typedef,所以它不能被覆蓋作爲一個函數,謝謝:)) – 2013-07-20 12:41:42

+7

@AlexanderVarwijk:你想'typedef',以避免需要結構'或'enum' 。如果您使用的命名約定允許使用相同名稱的函數和類型,那麼您可以做的最好的事情就是檢查如何命名程序的元素。 – 2013-07-20 18:45:14

+1

我的命名約定應該確保不會發生,比對不起更安全:) – 2013-07-21 12:56:43

4

typedef,因爲它與其它構建體中,用來給一個數據類型的新名稱。在這種情況下,它是爲了使代碼更清潔主要完成:

struct myStruct blah; 

myStruct blah; 
48

在C(不是C++),你必須聲明結構變量,例如:

struct myStruct myVariable; 

爲了能夠使用myStruct myVariable;相反,你可以typedef的結構:

typedef struct myStruct someStruct; 
someStruct myVariable; 

您可以結合struct定義和typedef的IT在其中宣佈匿名structtypedef的IT一條語句。

typedef struct { ... } myStruct; 
+1

最後一塊代碼不等同於前面的代碼。在最後一行中,您將一個類型別名'myStruct'定義到一個未命名的結構中。兩個版本之間存在(非常)細微的差別。 – 2009-11-04 17:52:40

+4

dribeas:我在句子「......一個聲明**匿名結構**和......」的單句中說明了這個微妙的區別 – 2009-11-04 18:10:33

+1

真的,我似乎跳過了那部分:) – 2009-11-05 07:27:36

5

當您使用struct時區別進來。

你必須做的第一種方式:

struct myStruct aName; 

第二種方法允許你刪除關鍵字struct

myStruct aName; 
19

如果使用structtypedef,你總能寫

struct mystruct myvar; 

這是非法的,如果您使用typedef你不需要寫

mystruct myvar; 

struct前綴了。

+1

給點答案。 – 2017-06-03 14:41:51

1

對於後面的示例,在使用結構時省略了struct關鍵字。因此,在你的代碼隨處可見,你可以這樣寫:

myStruct a; 

,而不是

struct myStruct a; 

這節省一些打字,而可能是更具可讀性,但是這是一個品味問題

20

在C的結構,聯合類型說明符關鍵字和所列舉的都是強制性的,即你總是有前綴的TY pe的名稱(其標籤)與struct,unionenum在引用類型時。

您可以通過使用typedef來擺脫關鍵字,這是一種信息隱藏形式,因爲在聲明對象時實際類型將不再可見。

因此建議(見例如Linux kernel coding style guide,第5章)只有當 你真正想要隱藏這個信息,不只是爲了節省幾個按鍵做到這一點。

的時候應該使用一個typedef將其永遠只與相應的存取器函數/宏使用不透明類型的一個例子。

+2

Linux內核編碼風格指南供參考:http://www.kernel.org/doc/Documentation/CodingStyle – 2010-07-21 20:45:37

+2

Linux內核編碼風格文檔已被移至https://www.kernel.org/doc/Documentation/process /coding-style.rst – 2017-05-11 10:48:56

82

另一個不同之處不指出的是,給結構的名稱(即結構MYSTRUCT),您還可以提供前進的結構的聲明。因此,在其他文件中,您可以編寫:

struct myStruct; 
void doit(struct myStruct *ptr); 

而不必訪問定義。我的建議是你把你的兩個例子:

typedef struct myStruct{ 
    int one; 
    int two; 
} myStruct; 

這給你一個更簡潔的類型名字的便利,但仍允許您如果需要使用完整的結構名稱。

+1

有一些方法可以用typedef做前向聲明:'typedef struct myStruct myStruct;';然後(稍後):'struct myStruct {...};'。你可以在'typedef'之後使用它* * struct myStruct' *或者* myStruct'(但是類型是不完整的,直到定義)。 – 2015-02-10 14:20:28

+0

值得一提的是,儘管(概念上)自動輸入了定義標籤並在單個「符號空間」中放置了「SomeThing」和「Something」,但C++確實爲手動typedef SomeThing重新定義了明確的限制到'struct SomeThing' ... ...否則你可能會認爲這會產生一個關於碰撞名稱的錯誤。資料來源:http://stackoverflow.com/a/22386307/2757035我想這是完成的(稍微徒勞!)向後兼容的原因。 – 2016-04-09 16:52:02

+0

@underscore_d:當時,人們認識到代碼的價值可以與C和C++一樣好,並且希望避免在兼容性方面造成不必要的障礙。可悲的是,這種想法已不再流行。 – supercat 2017-04-15 20:53:15

4

下面的代碼與別名myStruct創建一個匿名結構:

typedef struct{ 
    int one; 
    int two; 
} myStruct; 

您不能引用它,而不別名,因爲你不指定結構的標識符。

4

不能使用向前聲明與typedef struct

struct本身就是一個匿名類型,所以你不要有一個實際的名稱轉發申報。

typedef struct{ 
    int one; 
    int two; 
} myStruct; 

像這樣的向前聲明將無法工作:

struct myStruct; //forward declaration fails 

void blah(myStruct* pStruct); 

//error C2371: 'myStruct' : redefinition; different basic types 
70

structtypedef是兩個完全不同的事情。

struct關鍵字用來定義,或指代,結構類型。例如:

struct foo { 
    int n; 
}; 

創建一個名爲struct foo的新類型。名稱foo標籤;這是有意義的,只是當它立即被struct關鍵字之前,因爲標籤和其他標識是在不同的的名稱空間。 (這與namespace的C++概念相似但限制更多)。

儘管名稱存在,但它並沒有定義新的類型;它僅爲現有類型創建一個新名稱。例如,給定:

typedef int my_int; 

my_intint一個新的名字; my_intint都是正好是是同一類型。同樣,鑑於上述struct定義,你可以寫:

typedef struct foo foo; 

類型已經有一個名字,struct footypedef聲明給出了相同類型的新名稱foo

的語法允許一個structtypedef合併成一個單一聲明:

typedef struct bar { 
    int n; 
} bar; 

這是一個常見的成語。現在你可以參考這個結構類型struct bar或只是bar

請注意,typedef名稱在聲明結束前不可見。如果結構包含一個指向自身,則必須使用struct版本來參考一下吧:

typedef struct node { 
    int data; 
    struct node *next; /* can't use just "node *next" here */ 
} node; 

一些程序員會使用不同的標識符的結構標記和typedef名。在我看來,這沒有什麼好的理由。使用相同的名稱是完全合法的,並且使得它們更清楚,它們是相同的類型。如果必須使用不同的標識符,至少使用一個一致的約定:

typedef struct node_s { 
    /* ... */ 
} node; 

(就個人而言,我更喜歡省略typedef並參考類型struct bartypedef節省一點打字,但它隱藏了一個事實如果你希望類型是不透明的,這可能是一件好事,如果客戶端代碼是通過名字引用成員n,那麼它不是不透明的;它顯然是一個結構,在我的意見認爲它是一種結構,但很多聰明的程序員在這一點上不同意我的觀點,請準備閱讀並理解以任何一種方式編寫的代碼。)

(C++有不同的規則。給定struct blah的聲明,即使沒有typedef,也可以將其類型稱爲blah。使用typedef可能會讓你的C代碼更像C++ - 如果你認爲這是一件好事。)

+2

這個答案幫助我更好地理解了爲什麼C89庫和Linux內核更可能使用'struct mystruct_t {}'而不是'typedef struct {} mystruct_t'。 – 2016-07-25 13:05:51

2

我看到一些澄清是爲了這個。 C和C++不會以不同的方式定義類型。 C++本來只不過是C上的一個附加集合。

幾乎所有C/C++開發人員今天都有這樣的問題:a)大學不再教授基礎知識,b)人們不理解定義和聲明之間的區別。

此類聲明和定義存在的唯一原因是,鏈接器可以計算結構中字段的地址偏移量。這就是爲什麼大多數人擺脫了實際寫入不正確的代碼 - 因爲編譯器能夠確定尋址。當有人試圖進行某些事情時會出現問題,例如隊列或鏈接列表,或者支持O/S結構。

聲明以'struct'開頭,定義以'typedef'開始。

另外,結構體具有前向聲明標籤和定義的標籤。大多數人不知道這一點,並使用前向聲明標籤作爲定義標籤。

錯誤:

struct myStruct 
    { 
    int field_1; 
    ... 
    }; 

他們只需要使用向前聲明標註的結構 - 所以現在的編譯器知道它 - 但它不是一個實際的定義類型。編譯器可以計算尋址 - 但這不是它打算如何使用的原因,因爲我將暫時顯示。

使用這種形式的聲明的人必須在實際上每次引用它時都必須使用「struct」 - 因爲它不是一種官方的新類型。

相反,不引用本身的任何結構,應該聲明和定義只有這樣:

typedef struct 
    { 
    field_1; 
    ... 
    }myStruct; 

現在,它是一個真正的類型,使用的時候可以在爲「MYSTRUCT」使用,而不必用「結構」這個詞加上它。

如果你想有一個指針變量到結構,則包括二次標籤:

typedef struct 
    { 
    field_1; 
    ... 
    }myStruct,*myStructP; 

現在你有一個指針變量到結構,習慣它。

FORWARD DECLARATION--

現在,這裏的花哨的東西,向前聲明是如何工作的。如果你想創建一個引用自身的類型,比如鏈接列表或者隊列元素,你必須使用前向聲明。編譯器不考慮定義的結構,直到它到達最後的分號,所以它只是在該點之前聲明。現在

typedef struct myStructElement 
    { 
    myStructElement* nextSE; 
    field_1; 
    ... 
    }myStruct; 

,編譯器知道,雖然它不知道整個類型是什麼呢,它仍然可以使用向前參考引用它。

請正確聲明和typedef你的結構。其實有一個原因。

+4

我不相信C++曾經是「一系列包含在C之上的東西」。第一個實現是cfront,它是一個將C++源代碼翻譯爲C源代碼的預處理器。我不同意你對結構使用typedefs的建議,特別是針對指針。在typedef後面隱藏指針類型可能很危險;它更好的恕我直言,使事實,這是一個指針明確使用'*'。在你最後的例子中,我很好奇使用typedef的一個名字('myStruct')和struct標籤的另一個名字('myStructElement')。 – 2016-05-07 21:44:22

+2

這很大程度上是一個文體偏好問題。我對你的大部分建議(這並不是說你錯了)相當不滿。請參閱[我的答案](http://stackoverflow.com/a/26389105/827263),瞭解我自己的偏好摘要(許多C程序員都共享這些偏好,但當然不是全部)。 – 2016-05-07 21:45:22