2015.9.10关于链表中结构体指针的错误

昨天用结构体指针写了一个双链表的程序,编译环境是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了。这个时候就对了。

 

最后,刚刚咨询了本科C语言老师关于(NODE*)(p->next)->prev=New;和((NODE*)(p->next))->prev=New;的不同,老师的原话是这样的:->的优先级高与强制类型转换 (NODE*)的优先级,这样就解释得了为什么前者仍然报错而后者不报错了。

因为->的优先级比强制类型转换(NODE*)优先级别高,这样前者就相当于将p->next->prev转换成NODE*,这样以来还是没有解决我们上述提到的问题——p->next是结构体内层的结构体,他没有成员变量prev,这样编译器仍然报错。而后者相当于是将p->next转换成NODE*,这时候NODE*是全局的NODE*也就是外层结构体,所以这时候p->next具有成员变量prev,这个时候编译器就不会报错了。

posted @ 2015-09-10 09:15  玩呀熊熊  阅读(344)  评论(0编辑  收藏  举报