用struct 理解typedef

我很难理解这段代码:

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

所以,我们正在使用typedef构建struct节点…我假设我们这样做是为了初始化struct而不需要“struct”关键字。

我想问一下为什么在struct定义中我们使用名称“node” 两次 (在开始和结束时)。

其次是typedef node * nodepointer; 指着。 在这种情况下是否有必要使用typedef? 这个表达式是node * nodepointer; 不平等?

问题是你一次做两件事:声明结构类型并将结构类型定义为名称。

对于typedef ,语法是

 typedef  name; 

其中某些类型可能是一个简单的类型,如intlong等,或者像struct foo这样的复杂类型,而且无论之前是什么类型的struct foo都已经声明过,或者它是在那个时候声明的。

所以

 typedef struct foo { int bar; } foo; 

对应于

 typedef  name; 

其中struct foo { int bar; }namefoo

至于第二个问题,

在这种情况下, node *namenodepointer ,所以

 typedef node * nodepointer; 

将typepofing一个nodepointer作为指向node的指针(你之前已经将typedefd作为struct node { ... }

首先,让我们在这里说清楚: typedef不是变量的声明。 它只是将新类型名称别名为现有类型说明符。

 typedef  new_type_name; 

所以这里发生的事情肯定会欺骗未经训练的眼睛。

struct node本身是一个名为nodestruct ,它有两个属性int data和一个struct node *next

我马上就会进入next ,但现在这很简单。

然后,周围的typedef接受该结构并为其指定名称node 。 为什么这样,你可能会问?

这是因为在C(与C ++不同)中,使用其标记名称的结构需要在类型前加上struct

 struct Foo { // ... }; Foo bar; // ERROR struct Foo bar; // OK 

使用typedef,我们使用名为nodetypename为结构命名。 这个例子应该让它更清楚一些。

 typedef struct Foo { // ... } FooType; Foo bar; // ERROR struct Foo bar; // OK FooType bar; // OK struct FooType bar; // ERROR 

请记住,您可以定义没有标签的结构,从而typedef它们。

 typedef struct { // ... } FooType; FooType bar; // OK 

虽然我会解释为什么你不能在你的例子中这样做。

示例中的属性struct node *next是不完整的类型 。 不完整的类型,简单地说,是已声明的类型(给定名称和类型,即struct node; ),但未定义 (给定’body’,即struct node { int foo; }; )。

除非您指向它们否则在定义不完整类型之前不能使用它们

 struct Foo; struct Foo bar; // ERROR: Foo is not defined. struct Foo *bar; // OK - it's just a pointer. We know the size of a pointer. struct Foo { // ... }; struct Foo bar; // OK - it's defined now. 

因此,通过类型struct node *next声明,我们已经有了struct node声明但没有定义 ,这使得一个指向struct node的指针成为可能。

您还可以推断,直接使用struct node不起作用:

 struct node { struct node next; // ERROR - `struct node` becomes infinitely big struct node *next; // OK - a pointer is usually 4/8 bytes (we at least know its size at this point) }; 

回答你的最后三个问题:

什么[ typedef node * nodepointer; ] typedef node * nodepointer; [指向[?]

没有。 它只是另一种类型的别名 (标签或“昵称”)。 它只是说类型nodepointer是一个node *

这也意味着任何需要node *东西都可以使用nodepointer ,反之亦然。

在这种情况下是否有必要使用typedef

没必要,没有。 你也可以在任何地方使用node *而不是nodepointer 。 大多数编码标准对类型定义类型指针类型(如您的示例)不满意,因为它会增加代码库的混淆(正如您在此问题中所certificate的那样!:))

是[]表达式node * nodepointer; 不平等?

不。 同样,请记住typedef只是指定引用相同类型的另一种方式。 它实际上并不创建任何内存,而是为现有类型提供新名称。

这个结构是自引用的,这意味着它包含一个指向自身的指针。

如果不使用标记node ,稍后如何指定指针类型。 因此需要第一个node

 struct node { struct node *ptr; }; // you can do this way too typdef struct node node; 

完成typedef需要最后一个node

通过typedef node * nodepointer; ,现在你可以使用nodepointer来表示node* 。 无论在哪里发生node* ,您都可以简单地用nodepointer替换

在这种情况下是否有必要使用typedef?

它是由你决定,

这个表达式是node * nodepointer; 不平等?

不,这只声明nodepointer指针变量,typedef通过名称nodepointer创建typenode *nodepointer

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

第一行中的node是结构的标记

最后一行中的node是类型的名称。

这两者不必相同。

如果您希望它们不同,代码将如下所示。

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

类型的名称是node 。 结构的名称是helloNode

有关更多技术说明,这两个nodes位于不同的名称空间中 。 并且可以有不同的价值观。

来自http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf的第6.3节

如果在翻译单元中的任何点处可见多于一个特定标识符的声明,则句法上下文消除了引用不同实体的用法的歧义。 因此,各种标识符类别都有单独的名称空间,如下所示:

– 标签名称(通过标签声明和使用的语法消除歧义);

– 关键字struct,union或enum)的结构,联合和枚举的标记(通过遵循any32消除歧义);

– 结构或工会的成员; 每个结构或联合为其成员都有一个单独的名称空间(通过。或 – >运算符用于访问成员的表达式的类型消除歧义);

– 所有其他标识符,称为普通标识符(在普通声明符中声明或作为枚举常量)。

对于struct,您要定义结构名称(在大括号之前),然后是类型名称(在大括号之后):

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

下一个创建另一个类型,指向节点的指针:

  typedef node* nodepointer; 

现在两者都可用于创建变量:

  node myNode; nodepointer aPtr; 

Daleisha正确地说你需要struct标签才能声明next指向这些结构的指针。 在引入之前,您无法写下该类型名称。 另一种可能性是向前声明结构:

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

但是,这引入了一个或多个node更多提及。

作为一个副作用,引入struct标签“node”使得仍然可以编写正常的struct node ,而typedef本身是不可能的。

第二个typedef仍然是typedef,它不引入变量而是引入类型名称。 在您的源代码段中的所有声明之后,您可以说

 struct node *np1; node *np1; nodepointer np1; 

这些都是等价的。

两个类型名称nodestruct node的共存是可能的,因为“结构标记”(如“结构节点”中的“节点”),因为它们被调用,与其他名称分开保存,因此可用于另外,没有碰撞就命名别的东西。 要键入一个结构,以便新类型具有结构标记的名称是一个常见的例子,但名称“node”可以用于其他任何东西(int变量,无论如何)。