我通常是这样在C语言中为链表定义Node的:
typedef struct _Node {
int value;
struct _Node * next;
} Node;
通过一些测试,我发现我能够通过一个匿名结构体使这个定义工作,像这样:
typedef struct {
int value;
struct Node * next;
} Node;
是否有一个原因,为什么这工作?我觉得它不应该工作,因为Node在结构定义中还不是一个类型。
我的假设是结构Node * next是一个匿名结构本身,只有一个指向Node的指针。但如果是这种情况,那么我如何能够使用这里的节点类型,如果类型定义尚未解决?
这似乎只是工作。"tag"名称空间和"普通标识符"的名称空间;在C中是完全不同的(c++的规则略有不同)
匿名struct
的成员next
指向由同一声明前向声明的不完整struct Node
。
您的typedef
命名了一个匿名的struct
,它与struct Node
类型不同。所以你的指针next
指向一个编译器只知道它是struct
的类型。
避免这种头痛的一个好策略是将struct Node
和typedef
转发声明为相同的:
typedef struct Node Node;
^^^^ ^^^^
tag typedef name
和
struct Node {
...
};
此声明
typedef struct {
int value;
struct Node * next;
} Node;
引入了两个结构说明符。第一个是未命名的结构体,其类型定义名为Node
。第二个是命名结构体struct Node
。
这两个结构说明符不同。
例如,当你试图将Node *
类型的指针赋值给struct Node *
类型的指针时,编译器会发出警告或错误,反之亦然,因为存在使用不兼容的指针类型。
考虑下面的演示程序。
#include <stdlib.h>
#include <stdio.h>
typedef struct {
int value;
struct Node * next;
} Node;
int main( void )
{
Node *head = malloc( sizeof( Node ) );
head->value = 1;
head->next = NULL;
Node *next = malloc( sizeof( Node ) );
next->value = 2;
next->next = NULL;
head->next = next;
}
对于语句
head->next = next;
,例如编译器GCC 11.2会发出以下警告
<source>:18:16: warning: assignment to 'struct Node *' from incompatible pointer type 'Node *' [-Wincompatible-pointer-types]
18 | head->next = next;
| ^
ASM generation compiler returned: 0
<source>: In function 'main':
<source>:18:16: warning: assignment to 'struct Node *' from incompatible pointer type 'Node *' [-Wincompatible-pointer-types]
18 | head->next = next;
|
此外,您将无法解引用类型为struct Node *
的指针,因为struct Node
类型是不完整类型。struct Node
的定义是未知的。
例如,如果您将在上面的演示程序后面加上语句
printf( "%dn", head->next->value );
编译器此时将发出以下错误消息
<source>:21:31: error: invalid use of undefined type 'struct Node'
21 | printf( "%dn", head->next->value );
| ^~
你可以写成
typedef struct Node Node;
struct Node {
int value;
Node * next;
};