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

时间:2019-02-18 18:20:39

标签: c struct typedef

考虑以下代码:

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,我知道通常不应将其视为可靠的来源。但是,这样做有什么充分的理由吗?

1 个答案:

答案 0 :(得分:5)

人们使用诸如struct _Node之类的名称来故意忽略标准中设置的规则(或更常见的情况是,因为他们不知道标准中设置的规则)。粗略地讲,该标准说,以下划线开头的名称主要保留给“实现”,即编译器和系统库。有关详细信息,请参见C11 §7.1.3 Reserved identifiers

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

也请注意§6.2.1 Scopes of identifiers-重点已添加:

  

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

也请注意,POSIX也保留了_t后缀-请参见The Compilation Environment

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

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


要解决扩展的问题,

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

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

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

您还可以找到先例,在圣经中使用结构标签的替代名称和相应的typedef名称-表示Kernighan和Ritchie的“ C编程语言”。 (有趣的是,他们的示例在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用于指针。参见Is it a good idea to typedef pointers?