为什么在typedef结构体时要使用不同的标识符?

3

考虑以下代码:

typedef struct _Node Node;

struct _Node {
    struct _Node * node;
};

或者这样:
typedef struct _Node {
    struct _Node * node;
} Node;

有什么理由不将它们重写为

typedef struct Node Node;

struct Node {
    Node * node;
};

并且

typedef struct Node {
    Node * node;
} Node;

据我所知,它们是等效的。那些下划线前缀的原因是什么?我还看到其他变体,其中不是下划线,而是在第一次出现时大写字母,在第二次出现时小写字母,或者其他使它们不同的东西。一个例子在这里:
typedef struct Books {
   char title[50];
   char author[50];
   char subject[100];
   int book_id;
} Book;

这个例子来自于tutorialspoint,虽然他们通常不被认为是可靠的来源。但是这样做有什么好处吗?


可能是为什么在结构体中使用不同的标签和typedef?的重复问题。 - Gilles 'SO- stop being evil'
1个回答

5
人们使用像struct _Node这样的名称,是为了故意忽略标准中设定的规则(或者更经常的是因为他们不知道标准中设定的规则)。大体上,标准规定以下划线开头的名称主要保留给“实现”(即编译器和系统库)。有关详细信息,请参见C11 §7.1.3 保留标识符

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

还请注意§6.2.1 标识符的作用域-强调添加:

一个标识符可以表示对象、函数、标签或结构、联合或枚举的成员、typedef名称、标签名称、宏名称或宏参数。同一个标识符在程序的不同位置可以表示不同的实体。枚举的成员称为枚举常量。宏名称和宏参数在此不再考虑,因为在程序翻译的语义阶段之前,源文件中任何宏名称的出现都将被替换为构成它们的宏定义的预处理记号序列。
此外,请注意 POSIX 也保留了 _t 后缀 - 参见 编译环境
话虽如此,思考过程似乎是“这个名称不应该经常使用;用下划线作为前缀来防止使用”。还有“我看到它在系统头文件中使用了;我会复制那种风格”,没有意识到系统头文件是这样编码的,以避免踩在普通程序员保留的命名空间上,并使用保留给实现的命名空间。
你的改写很明智;这是我通常做的事情。

针对扩展问题:

并非所有人都知道结构标签与普通标识符不在同一命名空间中,因此他们不知道typedef struct Book Book;是完全安全和明确的(第一个Book在标签名称空间中,必须在前面加上struct;第二个Book在普通标识符名称空间中,必须在前面加上struct)。

此外,人们看到系统头文件并查看它们的用法,认为他们应该复制那里的样式,而不知道实现对其可使用的名称有不同的规则。

请注意,Linux内核编码标准不鼓励使用结构类型的typedefs;它们要求你在任何地方都使用struct WhatEver。这条规则有一些优点和一些缺点——自我一致性可能比使用哪种约定更重要。这意味着对于现有项目,“随波逐流”,但对于自己的新项目,无论你选择哪种方式,只要保持一致即可。

你还可以在经典著作《C程序设计语言》中找到使用结构标签和相应的typedef名称的替代名称的先例。(有趣的是,在1978年第一版和1988年第二版之间,他们的示例发生了很大变化。)
第二版:
typedef struct tnode *Treeptr;

typedef struct tnode {
    …
    Treeptr left;
    Treeptr right;
} Treenode;

第一版:

typedef struct tnode {
    …
    struct tnode *left;
    struct tnode *right;
} TREENODE, *TREEPTR;

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

@TimRandall:我已经扩展了它,并淡化了(而不是删除)“任性”的方面。我认为无知和误导性地从系统头文件复制更多地是罪魁祸首,而不是实际的任性。我希望这能解决你的问题。 - Jonathan Leffler
@Broman:我已经回答了你更广泛的问题——希望这可以解释清楚。我怀疑在SO上已经有一个(或两个或三个)重复的问题;有时候给出答案比找到重复的问题更简单。 - Jonathan Leffler
这个答案大多离题,没有解释为什么使用不同的标识符的做法会出现。一个早期相同的问题有一些涉及到这个问题的答案。 - Gilles 'SO- stop being evil'
为什么不这样写呢?struct Node { Node * node; }; - Fantastory
1
因为这是C语言而不是C++,@Fantastory。在使用typedef struct Node Node;或等效语句创建之前,没有名为“Node”的类型。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接