簡體   English   中英

C 中的 struct 和 typedef struct 之間的真正區別是什么?

[英]What is the real difference between struct and typedef struct in C?

定義結構體有兩種主要方式:

struct triangle_s {
    int a,b,c;
};

typedef struct triangle_s {
    int a,b,c;
} triangle;

已經被問過很多次了,但每個答案都是關於在使用typedef變體時不必多次編寫struct 除了可以避免重復struct關鍵字之外,還有什么真正的區別嗎? 我聽說你永遠不應該在 C 中使用typedef變體,但沒有人說為什么。

沒有區別, typedef只是刪除了使用struct前綴變量聲明的要求。

默認情況下是否使用typedef結構是一場宗教戰爭,主要是由手頭有太多時間的人進行的。 在現有代碼庫中工作時,您應該執行編碼標准或周圍代碼所做的任何操作。 對於您自己的個人代碼,請隨心所欲。

沒有“typedef 結構”這樣的東西。

第一部分: struct類型

struct引入了一種結構,它是由一組命名成員組成的聚合數據類型。 (數組也是聚合,但它們由許多被索引的相同成員組成。聯合有一組成員名稱,但一次只能包含一個成員,因此它們不是聚合。您可能不需要我知道。)

結構類型通常有標簽,因此實際的類型名稱將類似於struct Triangle 標簽 ( Triangle ) 與標識符不在同一個命名空間中,因此使用也用於其他目的的標簽沒有問題。 有些人喜歡用_s_u附加標簽,分別表示它們是結構或聯合標簽,但這不是我的風格; 我更喜歡在 CamelCase 中命名類型,對我來說,結構或聯合標記代表類型名。 但是當然您可以自由使用自己的約定。

如果您在程序中使用struct SomeTag ,您實際上是在聲明存在一個其標記為SomeTag的結構。 您不需要通過命名或描述結構的成員來填寫聲明,除非您需要在代碼中引用它們。 其成員尚未(尚未)聲明的結構稱為不完整的,但它仍然可以用作指針類型的一部分,因為 C 標准保證所有結構指針都具有相同的格式,而不管結構的內容如何。 (這並不使它們可以互換,但它確實意味着編譯器知道指針有多大。)從未定義其成員並且僅用作指針類型的目標的結構稱為opaque

您可以通過添加成員聲明塊來完成結構的聲明。 所以

struct Triangle {
    int a,b,c;
};

首先聲明存在一個名稱為struct Triangle ,然后通過聲明三個都是int的命名成員來填充該結構的定義。

順便說一下,聯合聲明和定義都非常相似。

結構定義可以在聲明中使用,就好像它是一個類型名稱一樣。 或者換句話說,您可以聲明結構類型的標記,立即填寫字段,然后聲明該類型的一個或多個變量:

struct Triangle { int a, b, c; } aTriangle, anotherTriangle;

這不是一種非常常見的風格,但重要的是要知道語法是可能的。

最后,定義一個結構而不給它一個標簽是合法的。 無標簽結構類型有一個怪癖:通常沒有兩種結構類型可以具有相同的標簽,但所有無標簽結構都是不同的。 這意味着您可以聲明一個結構類型,它實際上沒有名稱,並且不同於任何其他結構類型,甚至是具有完全相同成員的結構類型。 如果您有一個只有一個實例(“單例”)的聚合,這可能會稍微有用,盡管我自己不會真正使用這種樣式。 但經過一小段彎路后,我們將看到此功能的另一種用途。

第二部分:類型別名

C 類型名稱可能非常復雜,因為它們可以由多個部分組成。 例如, const struct Triangle*[8]是一個包含八個成員的數組,每個成員都是一個指向不可修改的struct Triangle的指針。 double (*)(const struct Triangle*[8])是一個函數,它接受一個這樣的數組作為參數(或者,更准確地說,它接受一個指向這樣一個數組的第一個元素的指針,因為數組到指針衰變。但這在這里無關緊要。)

為了使復雜類型更易於使用,C 允許您為類型聲明別名。 別名使用typedef聲明,否則看起來與變量聲明完全一樣。 因此,例如,您可以聲明一個int類型的變量

int someNumber;

因此你可以為int類型聲明一個別名

typedef int someType;

類似地,您可以聲明一個包含八個指向const Triangle元素的指針的數組

const Triangle* eightSlices[8];

以完全相同的方式,您可以為此類數組的類型聲明一個名稱:

typedef const Triangle* EightSlices[8];

請注意,類型的名稱恰好位於對象名稱所在的位置,它可以位於聲明中間的某個位置。

第三部分:以上兩者合二為一

作為聲明類型別名的簡單示例,以下是為結構類型聲明別名的方法:

一個不完整的結構定義:

typedef struct Triangle Triangle;

或者一個完整的結構定義:

typedef struct Triangle {
   int a, b, c;
} Triangle;

或者兩者都分開(這些可以按任何順序進行):

typedef struct Triangle Triangle;
struct Triangle {
    int a, b, c;
};

請記住,結構標記和其他標識符(例如類型別名)在不同的命名空間中,因此上面Triangle的兩種用法沒有沖突。 一些程序員覺得有必要區分它們,即使其中一個只能緊跟在struct之后使用,另一個不能在struct 其他人——我想你可以猜到我屬於這群人——發現故意為兩者使用相同的名稱很方便,依靠單詞struct的存在或不存在來讓我們知道它是類型別名還是一個標簽。 (而且,更常見的是,表明我們無意再次使用該標簽。)

所以,回到我的開場白:

沒有“typedef 結構”這樣的東西。

而且沒有。 我們這里有一個非常普通的struct ,聲明和定義。 我們有一個非常普通的類型別名,它為該struct提供了一個替代名稱。 就是這樣。

請注意,您可以為匿名類型(例如無標記結構類型)提供別名,之后該類型就不再是匿名的。 所以有些人會在上面的定義中省略標簽:

typedef struct {
    int a, b, c;
} Triangle;

這看起來很像上面提到的單例結構類型,但由於它是一種可以用於聲明多個實例的類型。 但我實際上並不推薦這種風格。

各自為政:一個可忽略的附錄

每個人都有自己的風格偏好,而這些偏好大多是有效的。 我們大多數人都參與過不止一個項目,而且由於每個項目都有自己的風格指南,我們需要學習如何在不同的項目中接受和使用不同的風格。 但是我認為我們中的大多數人都有一些我們覺得最舒服的風格,當我們為自己編寫代碼時(或者當我們開始一個項目以吸引准備符合我們的同事的意圖時,我們會恢復這種風格)風格)。 我上面使用的是我的風格。

事實上,我盡量避免使用壓縮聲明+定義+別名的語法,更喜歡上面顯示的兩個聲明版本:

typedef struct Triangle Triangle; /* type alias */
struct Triangle {
  int a, b, c;
};                                /* type definition */

我更喜歡這樣做的原因是它允許我使用自引用成員定義類型,例如鏈表和樹:

typedef struct TriangleList TriangleList;
struct TriangleList {
    Triangle      slice;
    TriangleList* next;
};

(如果我沒有對類型進行前向別名化,我將不得不將成員聲明為struct TriangleList* next;這會導致更難看的對齊。)

有時我最終會得到相互引用的類型,在這種情況下,我需要在任何結構定義之前將別名收集在一起。 這也可能是有利的,因為別名定義允許不透明地使用指向類型的指針,因此可以放置到根本不包含類型定義的公共標頭中。

但這只是我。 隨意忽略它。

定義結構體有兩種主要方式

不,只有一個:

struct triangle_s {
    int a,b,c;
}

. 這樣的定義可以出現在多種上下文中,包括但不限於您提供的兩種情況,但重要的是要了解您提供的兩種語句都不僅僅是結構定義。

不可否認,這個“更多”有點空洞:

 struct triangle_s { int a,b,c; };

它是結構類型該類型的零對象的聲明。 在右大括號和分號之間有一個標識符列表,在這種特殊情況下它恰好是空的。 但也可能是這樣,例如:

struct triangle_s {
    int a,b,c;
} triangle;

,它定義了類型struct triangle_s並將triangle聲明為該類型的對象。 當然,這將我們引向第二個例子

typedef struct triangle_s { int a,b,c; } triangle;

,這與前面的完全相同,除了typedef關鍵字指示聲明的標識符是類型別名而不是對象標識符。 同樣,結構定義只是從struct關鍵字到關聯的右大括號的部分,請注意typedef可用於聲明任何類型的別名,而不僅僅是結構。

除了可以避免重復 struct 關鍵字之外,還有什么真正的區別嗎?

無論哪種方式,您都有struct triangle_s的定義,並且您可以以任何一種方式使用其類型名稱的該形式。 在后一種情況下,你也可以用triangle (無論它是在范圍內)作為一個別名struct triangle_s ,對於任何和一切可以由形式送達目的struct triangle_s ,在意義上沒有區別。 這就是typedef

我聽說你永遠不應該在 C 中使用 typedef 變體,但沒有人說為什么。

有些人從風格上反對第二種形式,因為它在同一個聲明中混合了兩種不同的東西——結構類型的定義和它的別名聲明。 這條推理線繼續說,如果你想要typedef ,那么你應該單獨聲明它:

struct triangle_s {
    int a,b,c;
};

typedef struct triangle_s triangle;

然而,這又是一個代碼風格問題,而不是功能問題。

關於typedef的使用,還有其他一些風格點,我個人對此有更強烈的感受。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM