在C中使用typedef结构时,取消引用指向不完整类型错误的指针
#include 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程序,我得到了正确的输出。 但是如果我包含注释行,它会生成解除引用的不完整类型错误。 任何人都可以解释原因吗?
这是命名混乱。
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
),它可能不喜欢this =
operation。
更明确的类型转换将解决(*): e = (node*)(e->link);
现在这不再是沉默,程序员负责,警告将消失。
e->link
肯定存在,因此编译器可以获取此值。 但是s->link->num
不存在,因为s->link
是struct incomplete_s*
,我们不知道它是什么,所以我们不知道它是否有一个num
成员。
(*) Overkill补充 :在某些更高的优化级别,这仍然不会很好: 严格的别名可能会妨碍。 因此,将指针类型解引用到另一个指针类型仍然是一个危险的操作。
首先你应该修改你的结构,并且应该解决注释部分的错误:
typedef struct data { int num; struct data *link; }node;
当您在以下行中定义s和new时,不需要将它们等于NULL,这是正常工作的主要function:
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; }
struct node
在此代码中未声明,因此struct node*
是一个不完整的类型。
我猜struct node *link;
在struct的声明中应该是struct data *link;
。
最简单的答案是:您定义了一个需要一个未被声明的结构的类型。
您在注释行上获得错误的原因是因为它是您在节点结构中使用链接成员的成员的唯一行。 在代码的任何其他部分,你不会像你那样点击它,例如, e = new->link;
编译器此时不需要知道结构的成员,因为你说e
是node类型,node是结构而new->link
是兼容的结构,所以一切都好。
这也是你改变typedef struct data { …};
时的原因typedef struct data { …};
to typedef struct node { …};
一切正常,因为当编译器需要有关结构节点的信息时,它会找到一个名为node的结构,并且有一个名为link的成员,其成员名为num。 如您所见,typedef只是一种别名。 希望它能帮助你理解。