簡體   English   中英

在有或沒有指針,有或沒有列表的情況下,decleration/definition 塊之前或之后的 typedef 的差異?

[英]Difference of typedef before or after block in decleration/definition with or without a pointer, with or without a list?

每種風格的優勢是什么? 包含指針如何改變事物?

一個在標簽之前,另一個在 object 標識符之前。 Typedef 建立在它們的前身之上,這些前輩最終源自一些原始類型安排。

clang 可接受以下內容:

    typedef struct typeA {int i;} typeA;

    struct typeB {int i;} typedef typeB;

    typedef struct typeAa {int i;} * typeAa;
    struct typeBb {int i;} typedef * typeBb;
    
    typedef struct typeC {} typeC;
    struct typeD {} typedef typeD;    
    typedef struct typeCc {} * typeCc;
    struct typeDd {} typedef * typeDd;    
    
    typedef struct typeE {} typeE, typeEptr[], * typeEArrayptr, typeEptrArrayfunc();

謝謝!

在搜索前后無法在 [c][typedef][structs] 中找到任何結果,這就是全部結果:

在另一個結構中聲明一個結構(已經進行了類型定義)?

問題包含代碼:

typedef struct typeA {int i;} typeA;

struct typeB {int i;} typedef typeB;

typedef struct typeAa {int i;} * typeAa;
struct typeBb {int i;} typedef * typeBb;

typedef struct typeC {} typeC;
struct typeD {} typedef typeD;    
typedef struct typeCc {} * typeCc;
struct typeDd {} typedef * typeDd;    

typedef struct typeE {} typeE, typeEptr[], * typeEArrayptr, typeEptrArrayfunc();

據稱clang接受了它。 在某種程度上,這是准確的:如果沒有指定警告選項,GCC 和clang都會毫無怨言地接受代碼。 添加一組相當全面的警告選項,你會得到很多錯誤:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -pedantic --pedantic-errors -c td17.c
td17.c:4:12: error: ‘typedef’ is not at beginning of declaration [-Werror=old-style-declaration]
    4 |     struct typeB {int i;} typedef typeB;
      |            ^~~~~
td17.c:7:12: error: ‘typedef’ is not at beginning of declaration [-Werror=old-style-declaration]
    7 |     struct typeBb {int i;} typedef * typeBb;
      |            ^~~~~~
td17.c:9:20: error: struct has no members [-Wpedantic]
    9 |     typedef struct typeC {} typeC;
      |                    ^~~~~
td17.c:10:12: error: struct has no members [-Wpedantic]
   10 |     struct typeD {} typedef typeD;
      |            ^~~~~
td17.c:10:12: error: ‘typedef’ is not at beginning of declaration [-Werror=old-style-declaration]
td17.c:11:20: error: struct has no members [-Wpedantic]
   11 |     typedef struct typeCc {} * typeCc;
      |                    ^~~~~~
td17.c:12:12: error: struct has no members [-Wpedantic]
   12 |     struct typeDd {} typedef * typeDd;
      |            ^~~~~~
td17.c:12:12: error: ‘typedef’ is not at beginning of declaration [-Werror=old-style-declaration]
td17.c:14:20: error: struct has no members [-Wpedantic]
   14 |     typedef struct typeE {} typeE, typeEptr[], * typeEArrayptr, typeEptrArrayfunc();
      |                    ^~~~~
td17.c:14:20: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
cc1: all warnings being treated as errors
$

在 C 語法中, typedef被視為存儲 class — 就像externstatic等 — 盡管還有一個專門針對typedef的部分。 還有一個未來方向§6.11.5 存儲 class 說明符,其中指出:

除了在聲明中的聲明說明符的開頭之外,存儲類說明符的放置是一個過時的功能。

因此,在您的問題中, typedef出現在聲明中的第一個單詞之外的每個變體都使用了 C 的過時功能,不應在新代碼中使用。 它仍然支持向后兼容,而不是因為它應該被使用。

有一個 Q&A Is it a good idea to typedef pointers? 大多數人的觀點是答案是“否——function 指針可能是個例外”。

空結構定義typedef struct typeC {} typeC; 違反§6.7.2.1 結構和聯合說明符的規則,特別是 ¶8 其中說:

…如果結構聲明列表不包含任何命名成員,無論是直接還是通過匿名結構或匿名聯合,行為是未定義的。 ……

因此,空括號會調用未定義的行為。 然而,除非它們受到迂腐選項的約束,否則編譯器決定他們將接受該聲明,即使它沒有由標准 C 定義。如此定義的結構類型幾乎沒有用。 由於{} ,類型是完整的,但為空。 事實上,如果您打印sizeof(typeC)或等效值(沒有警告選項),那么打印的結果是0 您可以使用指向類型的指針,但不能使用類型。

考慮對初始代碼的擴展:

#include <stdio.h>

typedef struct typeF
{
    size_t  size;
    struct typeC C;
    size_t  left;
} typeF;

int main(void)
{
    printf("sizeof(typeC) = %zu\n", sizeof(typeC));
    printf("sizeof(typeF) = %zu\n", sizeof(typeF));
    printf("offsetof(typeF, C) = %zu\n", offsetof(typeF, C));
    printf("offsetof(typeF, left) = %zu\n", offsetof(typeF, left));
    return 0;
}

在 64 位機器上,output 是:

sizeof(typeC) = 0
sizeof(typeF) = 16
offsetof(typeF, C) = 8
offsetof(typeF, left) = 8

請注意,Linux kernel 編碼標准不允許對結構使用typedef 所有結構都必須有一個標簽,並且在引用類型時必須始終使用struct tag

另一種觀點是,您始終對結構類型使用 typedef 名稱。 在簡單的情況下,這意味着您可以使用:

typedef struct
{
    int number;
    char *name;
} NumberName;

但是,如果您需要自引用指針(例如,對於鏈表),則該結構必須有一個標記。 您可以使用以下順序:

typedef struct SomeTag SomeTag;
struct SomeTag
{
    int number;
    char *name;
    SomeTag *next;
    SomeTag *prev;
};

使用這種形式,您只能使用SomeTag來引用這種類型。 如果您在某處編寫struct SomeTag ,它不會引用顯示的類型。 它可能引用在別處定義的不完整類型,但更可能是錯誤。

或者,您可以使用:

typedef struct SomeTag
{
    int number;
    char *name;
    struct SomeTag *next;
    struct SomeTag *prev;
} SomeTag;

使用這種形式,在 typedef 定義之后,您可以使用SomeTagstruct SomeTag來引用結構類型。 但是,由於在定義結構體時沒有引入typedef名稱,所以必須在結構體內部使用符號struct SomeTag *

雖然這不是強制性的,但為標記和 typedef 名稱使用相同的名稱是相當常規的。 沒有沖突; 它們位於不同的命名空間中(§6.2.3 標識符的名稱空間。C++ 自動提供 class 或類型名稱,當您定義 class、結構或聯合時,不帶classstructunion前綴,因此對兩者使用相同的標記是一致的與 C++。還有其他學派認為標簽名稱應該與類型名稱不同。一個這樣的約定是typedef struct SomeTag_s SomeTag_t; (使用不同的后綴)。注意 POSIX 保留_t后綴(參見§2.2 The編譯環境§2.2.2 名稱空間——尤其是“任何標頭”保留后綴_t的那一行)。

請注意,function 指針聲明typeEptrArrayFunc()等效於:

struct typeE typeEptrArrayfunc();

這聲明了一個 function 類型,它返回一個struct typeE並采用一個不確定的參數列表(但它不是一個可變參數 function 類型,在原型的末尾帶有, ... )。 它不是 C90 到 C17/C18 中的嚴格原型。 在 C23 中,它將等同於:

struct typeE typeEptrArrayfunc(void);

這是一個 function 類型,沒有 arguments。

類型typeEptrArrayfunc與指針或 arrays 無關,盡管名稱如此。 類似的注釋適用於typeEArrayptr — 該類型是“指向struct typeE的指針”,與 arrays 無關。

暫無
暫無

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

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