为什么在类型定义结构时使用不同的标识符?

klu*_*utt 3 c struct typedef

考虑以下代码:

typedef struct _Node Node;

struct _Node {
    struct _Node * node;
};
Run Code Online (Sandbox Code Playgroud)

或这个:

typedef struct _Node {
    struct _Node * node;
} Node;
Run Code Online (Sandbox Code Playgroud)

是否有任何理由不将其重写为

typedef struct Node Node;

struct Node {
    Node * node;
};
Run Code Online (Sandbox Code Playgroud)

typedef struct Node {
    Node * node;
} Node;
Run Code Online (Sandbox Code Playgroud)

据我所知,它们是等效的。这些下划线前缀的原因是什么?我还看到了其他变体,它不是下划线,而是第一次出现时使用大写字母,在第二次出现时使用小写字母,或者其他使它们有所不同的方式。一个例子在这里:

typedef struct Books {
   char title[50];
   char author[50];
   char subject[100];
   int book_id;
} Book;
Run Code Online (Sandbox Code Playgroud)

此示例来自tutorialspoint,我知道通常不应将其视为可靠的来源。但是,有什么充分的理由这样做吗?

Jon*_*ler 5

人们喜欢使用名称struct _Node来故意忽略标准中设置的规则(或者更频繁地使用,因为他们不知道标准中设置的规则)。粗略地讲,该标准说,以下划线开头的名称大多保留给“实现”,即编译器和系统库。有关详细信息,请参见C11§7.1.3保留标识符

  • 以下划线,大写字母或另一个下划线开头的所有标识符始终保留供任何使用。
  • 在普通和标记名称空间中,所有以下划线开头的标识符始终保留为具有文件范围的标识符。

还要注意§6.2.1标识符的范围 —重点:

标识符可以表示一个对象;功能; 标签,结构,联合或枚举的成员;typedef名称;标签名称;宏名称;或宏参数。相同的标识符可以表示程序中不同点的不同实体。枚举的成员称为枚举常量。宏名称和宏参数在此不再赘述,因为在程序翻译的语义阶段之前,源文件中所有出现的宏名称都被构成它们的宏定义的预处理令牌序列所代替。

还要注意POSIX也保留_t后缀-请参见《编译环境》

就是说,思考过程似乎是“不应过多使用该名称;在该名称前加下划线以阻止其使用”。还有“我已经在系统标头中看到它了;我将复制该样式”,但没有意识到系统标头是用这种方式编码的,以避免踩到为普通程序员保留的名称空间,并使用为实现保留的名称空间。

您的重写很明智;这是我通常所做的。


要解决扩展的问题:

并非所有人都知道结构标记与普通标识符位于单独的命名空间中,因此他们并不知道结构标记typedef struct Book Book;是完全安全且明确的(第一个Book位于标记名称空间中,并且必须在其前面struct;第二个Book位于普通标识符中)名称空间,且不得以)开头struct

同样,人们看着系统头文件,看看他们做了什么,并认为应该在此处复制样式,而没有意识到实现对它可以使用或不能使用名称使用不同的规则。

请注意,Linux内核编码标准不鼓励将typedef用于结构类型。他们要求您在struct WhatEver任何地方使用。该规则有一些优点和缺点–自洽可能比您使用的约定更重要。这意味着现有项目可以“顺其自然”,但是只要您保持一致,在自己的新项目中采用哪种方式都没关系。

您还可以找到先例,在圣经中使用结构标签的替代名称和相应的typedef名称,即Kernighan和Ritchie的“ C编程语言”。(有趣的是,他们的示例在1978年第一版和1988年第二版之间发生了很大的变化。)

第二版:

typedef struct tnode *Treeptr;

typedef struct tnode {
    …
    Treeptr left;
    Treeptr right;
} Treenode;
Run Code Online (Sandbox Code Playgroud)

第一版:

typedef struct tnode {
    …
    struct tnode *left;
    struct tnode *right;
} TREENODE, *TREEPTR;
Run Code Online (Sandbox Code Playgroud)

注意,现代风格倾向于避免将typedef用于指针。请参见使用typedef指针是一个好主意吗?