【问题标题】:Typedef struct in C Vs C++C 与 C++ 中的 Typedef 结构
【发布时间】:2015-08-09 11:12:36
【问题描述】:

这在 C++ 中会出错,但在 C 中不会:

typedef struct nodes
{
    int data;
    struct node *next;
}node;

它在 C++ 中给出以下错误。

/home/DS cpp/linkedlist.cpp|10|error: conflicting declaration ‘typedef struct nodes node’|
/home/DS cpp/linkedlist.cpp|9|error: ‘struct node’ has a previous declaration as ‘struct node’|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

为了让它在 C++ 中工作,我必须将其更改为:

typedef struct node
{
    int data;
    struct node *next;
}node;

我不明白为什么会这样,我想知道 C 和 C++ 中的执行顺序以便我理解。

【问题讨论】:

  • 你知道你不需要在 C++ 中使用typedef,对吧? “让它工作”的正确方法是struct node { int data; node* next;};
  • “执行顺序”与什么有什么关系?
  • @alk,在 C 中有效,struct node* 声明了一个新的类型,不同于struct nodes
  • @JonathanWakely, huhu, 代码块... :}
  • @MikeHousky:允许声明指向不完整类型的指针是必要的。

标签: c++ c struct typedef


【解决方案1】:

让我们稍微分析一下你的代码:

typedef struct nodes
{
    int data;
    struct node *next;
}node;

这声明并定义了struct nodes,一个有两个成员的类型,并声明了一个类型别名,所以我们只能将它称为node

现在,在 C++ 中,成员声明 struct node *next automatically forward-declares a type called node。这与您的 typedef 目标 node 冲突:就好像您试图给两种类型赋予相同的名称。

在 C 中,没有冲突,因为名为 node 的类型实际上只能称为 struct node

第二个 sn-p 起作用了,因为在解析成员声明时 struct node 已经存在,没有新类型被前向声明……而且你所做的只是在同一个 typedef 中重命名它声明,C++ 并不在乎,知道它都是相同的类型(struct T T;区别在于语法,而不是名称)。

[C++11: 7.1.3/3]: 在给定的非类作用域中,typedef 说明符可用于重新定义在该作用域中声明的任何类型的名称,以引用它已经引用的类型。 [示例:

typedef struct s { / ... / } s;
typedef int I;
typedef int I;
typedef I I;

——结束示例]

[C++11: 7.1.3/6]: 在给定范围内,typedef 说明符不得用于重新定义在该范围内声明的任何类型的名称以引用不同的类型。 [示例:

class complex { / ... / };
typedef int complex; // error: redefinition

——结束示例]

当然,在 C++ 中,这一切都没有实际意义,您只需编写:

struct node
{
   int data;
   node* next;
};

您不需要typedef-离开详细类型说明符 struct

【讨论】:

  • 当您必须深入了解一份 1,338 页的文档(库从第 424 页开始,因此语言部分仅占整个文档的三分之一)只是为了回答有关名称和范围的基本问题,你可以看到为什么有些人认为 C++ 太复杂了。 :^)
  • @MikeHousky:太荒谬了
  • 感谢您提出对 C++ 标签名称的限制。
  • @LightnessRacesinOrbit:听起来确实像。我也不得不提到 Java 8 语言规范是一个 788 页的 PDF,这只是语言,没有平台 API 或 VM 规范。然而奇怪的是,似乎没有人认为这使 Java 过于复杂。我不认为可以同时以完整和简单的方式指定严肃的编程语言。
  • @LightnessRacesinOrbit:至少在 99% 的情况下,实现起来肯定是非常复杂,而不是 使用 它。我认为 C++ 被认为是一头复杂的野兽,主要是因为 (1) 它经常被错误地用作具有类和手动内存管理的 C 以及 (2) 它比其他语言更接近系统,这自然会使您面临更多与系统相关的问题。 struct typedef 的技术细节在语言 POV 中很有趣,但肯定不会阻止程序员完成工作(以防万一:OP 已经解决了问题)。
【解决方案2】:

您给出的 C 示例应该是一个错误。您使用的标签名称 (node) 尚未使用 struct node 定义。

鉴于这两个选择,第二个是要使用的。我更喜欢经济一点:

typedef struct node_t
{
    int data;
    struct node_t *next;
} node_t;

在 C 或 C++ 中,标签名称有自己的命名空间,因此标签名称和 typedef 名称使用相同的名称是没有问题的。在 C 中,这允许您使用 node_tstruct node_t 来引用此结构类型。如果声明的类型名称不存在,C++ 将在标签名称中搜索类型名称,因此不需要上面的双重定义,但不会造成伤害。

在这两种语言中,在完全定义类型之前的任何时候都需要显式的struct node_t 版本,因此任何自引用和任何前向引用都将使用struct 版本。我更喜欢在头文件中使用它,主要是因为它减少了#include 指令顺序的问题。

PS:这确实可以在任何一种语言中工作(请参阅 LRIO 对 C++11 标准指针的回答),并且已在足够多的双语甚至纯 C++ 头文件中使用,它不太可能消失很快),所以这是一种非常简单的方法,适用于任何一种语言。

【讨论】:

  • 一个未命名的结构 typedef 会更好(在两种语言中)typedef struct {} my_struct_t;,不是吗?我不知道..你对此有何看法?有什么缺点吗?
  • @Serthy AFAIK,除非您有标签名称,否则您不能在 C 中使用前向引用来引用该 my_struct_t 类型。 struct my_struct_t *ptr 不起作用,如果没有看到 my_struct_t 的完整声明,my_struct_t *ptr 将不起作用。如果使用和定义在单独的头文件中,则存在包含顺序问题。如果引用是循环的并且在单独的标题中,则您有一个 insolvable 包含顺序问题。
  • 开头句不正确;代码是完全有效的,但非常不正统,C - 并且毫无疑问不是预期的。 typedef 语句引入了三个类型名称; struct nodes(在语句末尾完成)、nodestruct nodes 的同义词)和不完整类型 struct node。这种不完整的类型与struct nodes 是分开的; struct nodes 类型仅包含指向此其他类型的指针。您不能使用next 指针创建nodes 列表而不使用滥用强制转换; struct nodestruct nodes 不同。
  • 还要注意使用 POSIX reserves words ending _t(作为类型名称);在您自己的代码中使用约定不一定是一个好主意。
  • @JonathanLeffler 支持 POSIX 信息。我不知道……但 POSIX 不是标准 C,即使他们是最后一个大用户。至于第一个陈述,我对 C 和 C++ 都感到失望,因为显然允许实例化包含指向不完整类型的指针的结构。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多