[英]dereferencing pointer to incomplete type error when using typedef struct in C
#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程序,我得到了正確的輸出。 但是如果我包含注釋行,它會生成解除引用的不完整類型錯誤。 任何人都可以解釋原因嗎?
首先,你應該修改你的結構,並且應該解決注釋部分的錯誤:
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;
}
這是命名混亂。
由於typedef
node
== struct data
。
但是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補充 :在某些更高的優化級別,這仍然不是很好: 嚴格的別名可能會妨礙。 因此,將指針類型解除引用到另一個指針類型仍然是一個危險的操作。
struct node
在此代碼中未聲明,因此struct node*
是一個不完整的類型。
我猜struct node *link;
在struct的聲明中應該是struct data *link;
。
最簡單的答案是:您定義了一個需要一個未聲明的結構的類型。
您在注釋行上獲得錯誤的原因是因為它是您在節點結構中使用鏈接成員的成員的唯一行。 在代碼的任何其他部分,你不會像你那樣點擊它,例如, e = new->link;
編譯器此時不需要知道結構的成員因為你說e
是node類型,node是結構而new->link
是兼容的結構,所以一切都好。
這也是你改變typedef struct data { …};
to typedef struct node { …};
並且一切正常,因為當編譯器需要有關結構節點的信息時,它會找到一個名為node的結構,並且有一個名為link的成員,其成員名為num。 如您所見,typedef只是一種別名。 希望它能幫助你理解。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.