#include<stdio.h>
typedef struct data
{
int num;
struct node *link;
}node;
main()
{
node *s=NULL,*e=NULL,*new;
new=malloc(sizeof(node));
s=malloc(sizeof(node));
s->num=10;
s->link=new;
new->num=8;
new->link=NULL;
// printf("%d\n",s->link->num); //// error: dereferencing pointer to incomplete type
e=s;
while(e!=NULL)
{
printf("%d",e->num);
e=e->link;
}
}
对于上面的C程序,我得到了正确的输出。但是如果我包含注释行,它会生成解除引用的不完整类型错误。任何人都可以解释原因吗?
答案 0 :(得分:2)
首先,你应该修改你的结构,并且应该解决注释部分的错误:
typedef struct data
{
int num;
struct data *link;
}node;
当您在以下行中定义s和new时,不需要将它们等于NULL,这是正常工作的主函数:
void main(){
node *s,*e,*new;
e=NULL;
new=malloc(sizeof(node));
s=malloc(sizeof(node));
s->num=10;
new->num=8;
new->link=NULL;
s->link=new;
printf("%d\n",s->link->num); /// works fine without any error
e=s;
while(e!=NULL)
{
printf("%d\n",e->num);
e=e->link;
}
return;
}
答案 1 :(得分:1)
这是一个命名混乱。
node
== struct data
感谢typedef
。
但是node
!= struct node
。它们是两种不同的类型。并且struct node
尚未定义。
遗憾的是,C标准允许这种令人困惑的结构,甚至没有被编译器标记为警告。但这就是今天定义C的方式,所以我们必须忍受它。
我的建议:不要对struct
和typedef
使用相同的名称。创建自己的约定,例如struct thing_s
和typedef struct thing_s thing_t;
。这样可以避免命名这样的混淆。
解决方案现在非常明显。 替换:
typedef struct data { int num; struct node *link; } node;
通过
typedef struct data { int num; struct data *link; } node;
并且有问题的printf
现在可以使用。
但你的问题是:为什么你的程序在没有这个printf
的情况下工作了?
现在这是一个有趣的。
让我们回到最初的定义。
正如我们所说,struct node
不存在,因此是不完整的。
为了更好地遵循我们要解释的内容,我们将其称为struct incomplete_s
。现在,node
变为:
typedef struct data { int num; struct incomplete_s *link; } node;
如果没有问题printf
,它仍然有用。
原因是,node
已正确定义。它是一种已知尺寸和类型的结构。因为struct incomplete_s
不完整无关紧要,因为link
被定义为指向它的指针。你也可以定义void * link;
,它仍然有效。
void*
或struct incomplete_s *
或whatever *
具有相同的大小:它们都是指针。因此可以正确地创建托管它们的结构。
在你的主循环中,你的程序会:
while(e!=NULL)
{
printf("%d\n",e->num);
e=e->link;
}
是e
,它是一个指针node*
,取e->link
的值,这是一个指针struct incomplete_s *
。
请注意,两个指针都应指向不同的类型。 但它们都是指针,所以是的,这项任务在技术上是可行的并且由标准授权。
现在一个更谨慎的编译器可能会在这里发出警告,因为你不应该混合使用不同类型的指针。这是一个静默类型转换,它是未来错误的一个配方。
我不知道你使用的是哪个编译器,但是你可以提高它的警告级别(对于Visual使用“警告级别4”,对于gcc使用-Wall -Wextra
),它可能不喜欢这个=
操作
更明确的类型转换将解决(*):
e = (node*)(e->link);
现在这不再是沉默,程序员负责,警告将消失。
e->link
肯定存在,因此编译器可以获取此值。
但是s->link->num
不存在,因为s->link
是struct incomplete_s*
,我们不知道它是什么,因此我们不知道它是否有num
成员
(*)过度补充:在更高的优化级别,这仍然不太好:strict aliasing可能会妨碍。因此,将指针类型解引用到另一个指针类型仍然是一个危险的操作。
答案 2 :(得分:0)
struct node
,因此struct node*
是不完整的类型。
我猜结构声明中的struct node *link;
应为struct data *link;
。
答案 3 :(得分:0)
最简单的答案是:您定义了一个需要一个未被声明的结构的类型。
您在注释行上获得错误的原因是因为它是您在节点结构中使用链接成员的成员的唯一行。在你的代码的任何其他部分你都不会像你那样点击它,例如,e = new->link;
编译器此时不需要知道结构的成员,因为你说的是e
是node类型,node是结构,new->link
是兼容的结构,所以一切都好。
这也是将typedef struct data { …};
更改为typedef struct node { …};
并且一切正常的原因,因为当编译器需要有关结构节点的信息时,它会找到一个名为node的结构,并且一个名为link的成员,其成员名为num。如您所见,typedef只是一种别名。希望它能帮助你理解。