zoukankan      html  css  js  c++  java
  • 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("申请内存失败!
    ");
     return NULL;
     }
     else
     {
     Head->prev=NULL;
     Head->next=NULL;
     Length=0;
     printf("链表初始化成功!
    ");
     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("申请内存失败!
    ");
      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!
    ");
    	 return FALSE;
     }
     else
     {
     
         while(i<pos-1)
    	 {
    	 p=p->next;
    	 i++;
    	 }//while
    
    	 New=(NODE*)malloc(SIZE);
    
    	 if(NULL==New)
    	 {
    	   printf("申请内存失败!
    ");
           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
    ",i,p->dat.val_0,p->dat.val_1);
    	}
    	
    	printf("Test Over,Length of List=%d.
    
    
    ",Length);
    
    }
    

    如上,编译器报错:

    C:Documents and SettingsAdministrator桌面双链表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,这个时候编译器就不会报错了。

  • 相关阅读:
    【甘道夫】Hadoop2.2.0 NN HA详细配置+Client透明性试验【完整版】
    zookeeper学习资源
    Java jdbc数据库连接池
    几种任务调度的 Java 实现方法与比较
    java常用集合类详解(有例子,集合类糊涂的来看!)
    Eclipse上GIT插件EGIT使用手册
    MongoDB 数据备份、恢复与迁移管理
    Mongodb快速入门之使用Java操作Mongodb
    mongodb高可用集群搭建
    Mongodb常见错误
  • 原文地址:https://www.cnblogs.com/wan0807/p/4796787.html
Copyright © 2011-2022 走看看