我有这个代码:
#include<stdio.h>
#include<stdlib.h>
typedef struct{
int data;
struct node *next;
} NODE;
NODE *head = NULL;
void display_ll(NODE *head){
if (head == NULL){
printf("\nList is empty");
} else{
for(NODE *curr=head; curr!=NULL; curr=curr->next){
printf("%d ->", curr->data);
}
printf("\b\b ");
}
}
int main(void){
NODE *temp = (NODE *)malloc(sizeof(NODE));
temp->data = 5;
temp->next = NULL;
head = temp;
display_ll(head);
return 0;
}
在第 15 行,即我在循环curr=curr->next
中执行的操作for
,如果使用 clang 进行编译,则会收到警告,如果使用 gcc 进行编译,则会收到错误:
- 铛:
warning: incompatible pointer types assigning to 'NODE *' from 'struct node *' [-Wincompatible-pointer-types]
- 海湾合作委员会:
error: assignment to ‘NODE *’ from incompatible pointer type ‘struct node *’ [-Wincompatible-pointer-types]`
我想到了一种解决方法,我们将结构写为,typedef struct node {...} NODE;
这样就可以了。但我想知道是否有任何方法可以解决这个问题,而无需重新定义这个结构。
您的结构名称不是
node
。实际上,它是未命名的 (
struct{ ... }
),并且NODE
是该未命名结构的别名。因此在这一行中:
next
并不是如您所期望的指向当前结构的指针(它是指向此处向前声明的其他假定的指针)。struct node
为了修复此问题,只需为您的结构命名:
正如您所期望的,这将生成
next
一个指向当前结构的指针。Live demo
附注:
您应该始终启用并注意编译器警告。
display_ll
参数head
隐藏了全局警告。即使这是您想要发生的,也建议更改参数的名称以避免混淆。struct node
和NODE
是不同的东西。node
是标签,NODE
是别名。您需要将标签添加到您的结构声明中:
您没有
struct node
,您有一个未命名的结构。C 语言中的结构体相当令人困惑,因为它们可以以多种不同的方式使用。
要在 C 中定义结构体,你可以这样写
其中
node
是结构标签,一种特殊的标识符,存在于与普通变量不同的命名空间中。为了稍后基于该结构标签声明变量,我们必须struct node x;
使用struct
关键字 present 进行键入。如果我们写
那么这就是一个没有结构标记的匿名结构。此结构定义仅在此处可见 - 我们不能在其他地方声明同一匿名结构的变量。这意味着它是无用的 - 除非我们在末尾添加变量名:
现在我们可以定义一个匿名结构体,同时用 为其创建一个别名
typedef
,以便可以在任何地方声明该结构的变量:这里
node_t
不是变量名,而是类型的名称。以上可能是在 C 中定义结构体的最常用方法。如果我们使用那种风格,那么在执行自引用结构时就会遇到问题。因为无论我们使用哪种风格,结构定义直到我们到达末尾才算完整
};
。这意味着我们无法从结构定义内部引用它 - 因为定义尚未完成。为了解决这个问题,我们可以前向声明该结构:
这是一个不完整的类型,我们暂时还不能创建它的任何实例 - 直到我们稍后到达
};
定义时struct node { ... };
,类型定义才完成。然而,我们可以创建一个指向不完整类型的指针 - 即使我们还不能声明它的对象:
这就是为什么当我们忘记包含一些使结构定义可见的标头时,我们有时会收到奇怪的编译器错误,抱怨“类型不完整”。一旦编译器在同一个翻译单元内看到结构定义,我们就可以声明该类型的对象并访问成员。
使用与上述相同的技巧,我们可以定义如下结构:
这
struct node *next
是一个指向不完整类型的指针,即将在下一行完成。但是如果我们写
然后,编译器声明一个指向不完整类型的指针
struct node
,但这不是我们现在定义的结构。该结构是匿名的,没有结构标记,而且我们命名了类型名称node_t
- 它们都与没有任何联系struct node
。因此,编译器认为struct node
它是在其他地方定义的东西。因此
struct node*
不兼容,node_t*
因为据编译器所知,这是两个不同的结构。和以前一样,我们可以通过提供一个结构标签来解决这个问题:
现在
struct node* next
是一个指向不完整类型的指针struct node
,并且我们还有该类型的别名,称为node_t
。这意味着一旦定义和 typedef 到位,我们就可以互换使用struct node
和node_t
。此外,如果我们愿意的话,我们可以给予
typedef
和结构标签相同的名称,因为它们位于不同的名称空间:进一步阅读:
不完全类型对于实现私有封装非常有用:
如何在 C 中进行私有封装?
关于使用哪种结构体样式,存在一些风格之争。不过,这并非完全是主观的:
是否有技术原因导致选择一种结构体编码样式而不是另一种?