关于C#:用struct理解typedef

Understanding typedef with struct

本问题已经有最佳答案,请猛点这里访问。

我很难理解这个代码示例:

1
2
3
4
5
6
7
typedef struct node
{
        int data;
        struct node * next;
} node;

typedef node * nodepointer;

所以,我们正在使用typedef构建结构节点…我假设我们这样做是为了在不需要"struct"关键字的情况下初始化结构。

我想问一下为什么在结构定义中我们使用了两次"node"这个名称(从开始到结束)。

其次,typedef node * nodepointer;所指的。在这种情况下是否需要使用typedef?这个表达式node * nodepointer;不相等吗?


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

1
typedef <type specifier> new_type_name;

所以这里发生的事情对未经训练的人来说肯定是骗人的。

struct node本身是一个struct称为node,有两个属性int datastruct node *next

我一会儿会更多地了解next,但现在这已经足够简单了。

然后,周围的typedef采用该结构,并将其命名为node。你可能会问,这是为什么?

这是因为在C(不像C++)中使用结构的标签名需要用EDCOX1(2)来预定义类型。

1
2
3
4
5
6
struct Foo {
    // ...
};

Foo bar; // ERROR
struct Foo bar; // OK

使用typedef,我们将结构命名为typename,称为node。这个例子应该更清楚一点。

1
2
3
4
5
6
7
8
9
typedef struct Foo {
    // ...
} FooType;

Foo bar; // ERROR
struct Foo bar; // OK

FooType bar; // OK
struct FooType bar; // ERROR

请记住,您可以定义不带标记的结构,因此可以定义typedef

1
2
3
4
5
typedef struct {
    // ...
} FooType;

FooType bar; // OK

不过,我将在示例中解释为什么您不能这样做。

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

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

1
2
3
4
5
6
7
8
9
10
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是行不通的:

1
2
3
4
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)
};

回答最后三个问题:

What [does] typedef node * nodepointer; [point] to[?]

没有什么。它只是另一种类型的别名(标签或昵称)。它简单地说,nodepointer型实际上是node *型。

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

Is it necessary to use typedef in this case?

不需要,不。你可以在任何地方使用node *,而不是nodepointer。大多数编码标准对指针类型(如您的示例)的typedefing类型并不满意,因为它会给代码基增加混淆(正如您在这个问题中所演示的那样)。:)

Is [the] expression node * nodepointer; not equal?

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


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

如果您不使用标记node,以后将如何指定指针类型。因此,首先需要node

1
2
3
4
5
struct node {
    struct node *ptr;    
};
// you can do this way too
typdef struct node node;

最后一个node是完成typedef所必需的。

通过typedef node * nodepointer;,现在可以用nodepointer来表示node*。在发生node*的地方,可以简单地用nodepointer替换。

Is it necessary to use typedef in this case?

这取决于你,

Is this expression node * nodepointer; not equal?

不,这只声明nodepointer为指针变量,而typedef创建类型为node *的别名,名称为nodepointer


问题是,您同时要做两件事:声明结构类型和将该结构定义为名称。

对于typedef,语法是

1
typedef <some type> name;

其中有些类型可能是简单的类型,如intlong等,或是复杂的类型,如struct foo等,而不管struct foo是以前声明的还是当时声明的。

所以

1
typedef struct foo { int bar; } foo;

相对应

1
typedef <some type> name;

其中struct foo { int bar; }namefoo

关于第二个问题,

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

1
typedef node * nodepointer;

nodepointer作为指向node的指针进行typedef(您刚才将其typedef定义为struct node { ... })。


1
2
3
4
typedef struct node
    int data;
    struct node * next;
} node;

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

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

这两个不一定是相同的。

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

1
2
3
4
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节

If more than one declaration of a particular identifier is visible at any point in a
translation unit, the syntactic context disambiguates uses that refer to different entities.
Thus, there are separate name spaces for various categories of identifiers, as follows:

— label names (disambiguated by the syntax of the label declaration and use);

— the tags of structures, unions, and enumerations (disambiguated by following any32)
of the keywords struct, union, or enum);

— the members of structures or unions; each structure or union has a separate name
space for its members (disambiguated by the type of the expression used to access the
member via the . or -> operator);

— all other identifiers, called ordinary identifiers (declared in ordinary declarators or as
enumeration constants).


对于结构,您定义的是结构名(大括号前),然后是类型名(大括号后):

1
2
3
4
 typedef struct node {
   int data;
   struct node* next;
 } node;

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

1
 typedef node* nodepointer;

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

1
2
 node myNode;
 nodepointer aPtr;


Daleisha正确地说,您需要struct标记来声明指向这些结构的指针next。在引入该类型名称之前,不能将其写下来。另一种可能是转发声明结构:

1
2
3
4
5
6
7
8
struct node;
typedef struct node node;

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

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

作为一个副作用,引入结构标记"node"使得仍然可以编写普通的struct node,而单单typedef就不可能做到这一点。

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

1
2
3
struct node *np1;
node *np1;
nodepointer np1;

这些都是等效的。

两个类型名称nodestruct node共存是可能的,因为"结构标记"(如"结构节点"中的"节点")与其他名称分开持有,因此可用于命名其他名称,此外,不会发生冲突。对于typedef结构,使新类型具有struct标记的名称是一个常见的示例,但名称"node"可用于其他任何内容(int变量,随便什么)。