昨天用结构体指针写了一个双链表的程序,编译环境是VC6.0,之前写单链表的时候也是用的这个编译器,但是昨天出了一个让我很费解的问题,代码如下:
/********************************************************** * C语言实现双链表 *文件名:list.c *作者:Mr Wan *编写时间:2015.9.9 *功能:实现双链表的简单操作 *版本:1.0 * **********************************************************/ #include<stdio.h> #include<stdlib.h> #define SIZE sizeof(NODE)//每个节点所占内存的大小 typedef enum { FALSE=0, TRUE=1 }BOOL;//自定义BOOL类型 typedef struct { int val_0;//数据val_0 int val_1;//数据val_1 }DAT;//节点的数据域 typedef struct { DAT dat;//节点的数据域 struct NODE* prev;//前置节点地址 struct NODE* next;//后驱节点地址 }NODE;//定义节点结构体 static unsigned int Length=0;//链表的长度,即出去零节点外之后的所有节点数 NODE* InitList(void); BOOL AddNode(NODE* Head,DAT dat); BOOL AddNodeAt(NODE* Head,unsigned int pos,DAT dat); void ListTest(NODE* Head); void main(void) { NODE* List=NULL; DAT dat; dat.val_0=1; dat.val_1=13; List=InitList(); AddNode(List,dat); ListTest(List); AddNode(List,dat); ListTest(List); AddNode(List,dat); ListTest(List); } /********************************************************** * NODE* InitList(void) *功能:初始化一个链表,并将链表头指针返回 *输入:void *输出:链表的头指针 * **********************************************************/ NODE* InitList(void) { NODE* Head=(NODE*)malloc(SIZE); if(NULL==Head) { printf("申请内存失败!\n"); return NULL; } else { Head->prev=NULL; Head->next=NULL; Length=0; printf("链表初始化成功!\n"); return Head; } } /********************************************************** * BOOL AddNode(NODE* Head,DAT dat) *功能:在一个已知的链表最后添加一个节点 *输入:已知链表的表头指针,待添加节点的数据域 *输出:TRUE--成功/FALSE--失败 * **********************************************************/ BOOL AddNode(NODE* Head,DAT dat) { unsigned int i=0; NODE* p=Head; NODE* New=NULL; while(i<Length) { p=p->next; i++; } New=(NODE*)malloc(SIZE); if(NULL==New) { printf("申请内存失败!\n"); return FALSE; } else { p->next=New; New->prev=p; New->next=NULL; New->dat=dat; Length++; return TRUE; } } /********************************************************** * BOOL AddNodeAt(NODE* Head,unsigned int pos,DAT dat) *功能:在一个已知的链表中的pos位置之前添加一个节点 *输入:已知链表的表头指针,添加的位置pos,待添加节点的数据域 *输出:TRUE--成功/FALSE--失败 * **********************************************************/ BOOL AddNodeAt(NODE* Head,unsigned int pos,DAT dat) { unsigned int i=0; NODE* p=Head; NODE* New=NULL; if(pos>Length) { printf("pos不应该大于链表的Length!\n"); return FALSE; } else { while(i<pos-1) { p=p->next; i++; }//while New=(NODE*)malloc(SIZE); if(NULL==New) { printf("申请内存失败!\n"); return FALSE; } else { New->next=p->next; p->next->prev=New; p->next=New; New->prev=p; New->dat=dat; Length++; return TRUE; } }//else } /********************************************************** * void ListTest(NODE* Head) *功能:链表的测试函数 *输入:待测试链表的表头指针 *输出:void * **********************************************************/ void ListTest(NODE* Head) { unsigned int i=0; NODE* p=Head; while(NULL!=p->next) { p=p->next; i++; printf("NODE%d的数据域中val_0=%d,val_1=%d\n",i,p->dat.val_0,p->dat.val_1); } printf("Test Over,Length of List=%d.\n\n\n",Length); }
如上,编译器报错:
C:\Documents and Settings\Administrator\桌面\双链表\list.c(168) : error C2037: left of ‘prev‘ specifies undefined struct/union ‘NODE‘
编译器提示:代码中p->next->prev=New;有错误。按照错误提示就是说,prev左边有未定义的NODE型结构体。这我就很纳闷了,prev左边是p->next,按道理说p->next也是NODE*型的,p->next->prev也是NODE*型的,而New也是NODE*型的,也就是说等号两边的变量类型是一致的,可是编译器还是报了错。
我跟着编译器提示的错误,尝试着将p->next进行了强制类型转换,如下:
(NODE*)(p->next)->prev=New;编译器还是提示错误,我又试着将上述代码该成了这样:((NODE*)(p->next))->prev=New;这个时候编译器不报错了,运行结果正常,这样我就又不理解了,上述两条代码有什么区别吗?假如有区别的话,应该区别就在于运算优先级的区别。那么我就不明白了上述两条代码的运算优先级有什么不一样。求高手解答。
上述这个问题先放在一边,我将代码copy到百度知道求高手指教,有网友回答如下:恐怕问题出在结构体模板定义之中,NODE还没有被声明,却在结构体体中应用了。还是为结构体起一个名字吧……
给结构体起一个名字?什么意思?我看了一下,我代码中关于结构体的定义,如下:
typedef struct { DAT dat;//节点的数据域 struct NODE* prev;//前置节点地址 struct NODE* next;//后驱节点地址 }NODE;//定义节点结构体
我将
struct
{
……
}
这个类型变为NODE型,但是上述结构体没有给他取名字,
我试着 将代码改成这样:
typedef struct NODE { DAT dat;//节点的数据域 struct NODE* prev;//前置节点地址 struct NODE* next;//后驱节点地址 }NODE;//定义节点结构体
这时候编译没有报错,结果正确。这样就证明上面那个网友说的是正确的。按照之前错误的写法,typedef只是将
struct
{
……
}
这个类型重新定义别名NODE型,然而在取别名之前要先把被取别名的对象定义好,即先对结构体进行定义:
struct
{
DAT dat;//节点的数据域
struct NODE* prev;//前置节点地址
struct NODE* next;//后驱节点地址
};
从上述代码中我们可以看到,在这个未取名的struct中,我们定义了两个struct NODE*的结构体指针,
接下来编译器才将未取名的struct取个别名叫NODE。
也就是说在给结构体取别名为NODE之前,在结构体中我们已经定义了两个struct NODE*的结构体指针,这样两个NODE之间就没有关系,这两个NODE不是一个NODE,结构体里面的NODE和重新给结构体取的别名NODE两者的作用域也是不一样的。也就是说代码中:p->next是结构体里面的那个NODE*类型,里面的那个NODE*和结构体NODE是不一样的,结构体NODE里面有三个成员变量,而结构体里面的那个NODE是没有成员变量的,这样以来p->next->prev就会出错,因为p->next是结构体里面的那个NODE*类型的,所以p->next是没有成员变量prev的。
所以后来我们将p->next强制转换成NODE*,这个时候就是将结构体里面的那个struct NODE*强制转换成和外面的结构体一样的类型,因为外面的结构体类型的作用域是全局的,所以在强制转换时,虽然有两种NODE*,但是由于结构体里面的NODE*是局部变量,所以强制转化是将被转换的对象转换成和外面的结构体一样的类型,而外面的结构体类型是有成员变量的,这样以来编译器就不报错了。
但是正确的做法不是这样的,经过和同学讨论,正确的应该是这样写:
typedef struct NODE
{
DAT dat;//节点的数据域
struct NODE* prev;//前置节点地址
struct NODE* next;//后驱节点地址
}NODE;//定义节点结构体
这就是说,我们先定义一个结构体
struct NODE
{
DAT dat;//节点的数据域
struct NODE* prev;//前置节点地址
struct NODE* next;//后驱节点地址
}
在这个结构体里面我们又定义了两个struct NODE*的变量,由于在这两个变量定义之前,我们已经对NODE进行了声明,所以里面的NODE和外面的NODE就是一样的了,然后我们再给外层的结构体取一个别名就NODE,这三个NODE就是一样的NODE了。这个时候就对了。
原文:http://www.cnblogs.com/wan0807/p/4796787.html