结构体中定义结构体:C/C 中结构体(struct)知识点强化



为了进学习结构体这重要知识点我们今天来学习下链表结构

   结构体可以看做是种自定义数据类型它还有个很重要特性就是结构体可以相互嵌套使用但也是有条件结构体可以包含结构体指针但绝对不能在结构体中包含结构体变量

   suct test
   {
   char name[10];
   float socre;
   test *next;
   };//这样是正确!
   suct test
   {
   char name[10];
   float socre;
   test next;
   };//这样是!

   利用结构体这点特殊特性我们就可以自己生成个环环相套种射线结构个指向另

   链表学习不像想象那么那么容易很多人学习到这里时候都会碰到困难很多人也因此而放弃了学习在这里我说,定不能放弃对应它学习我们要进行分解式学习思路方法很重要理解需要时间不必要把自己逼迫那么紧学习前你也得做些最基本准备工作你必须具备对堆内存基本知识了解还有就是对结构体基本认识有了这两个重要条件再进行分解式学习就可以比较轻松掌握这节内容难点

   下面我们给出个完整创建链表不管看懂看不懂希望读者先认真看看不懂没有关系我下面会有分解式教程但的前基本研究定要做要不即使我分解了你也是无从理解

   代码如下我在重要部分做了注解:

   #
   using s;

   suct test
   {
   char name[10];
   float socre;
   test *next;
   };

   test *head;//创建个全局引导进入链表指针

   test *create
   {
   test *ls;//节点指针
   test *le;//链尾指针
   ls = test;//把ls指向动态开辟堆内存地址
   cin>>ls->name>>ls->socre;
   head=NULL;//进入时候先不设置head指针指向任何地址,不知道是否上来就输入null跳出
   le=ls;//把链尾指针设置成刚刚动态开辟堆内存地址,用于等下设置le->next,也就是下个节点位置

   while(scmp(ls->name,\"null\")!=0)//创建循环条件为ls->name值不是null,用于循环添加节点
   {
   (headNULL)//判断是否是第次进入循环
   {
   head=ls;//如果是第次进入循环,那么把引导进入链表指针指向第次动态开辟堆内存地址
   }
  
   {
   le->next=ls;//如果不是第次进入那么就把上链尾指针le->next指向上次循环结束前动态创建堆内存地址
   }
   le=ls;//设置链尾指针为当前循环中节点指针,用于下次进入循环时候把上节点next指向上次循环结束前动态创建堆内存地址
   ls= test;//为下个节点在堆内存中动态开辟空间
   cin>>ls->name>>ls->socre;
   }

   le->next=NULL;//把链尾指针next设置为空,不管如何循环总是要结束,设置为空才能够在循环显链表时候不至于死循环
   delete ls;//当结束时候最后个动态开辟内存是无效,所以必须清除掉
   head;//返回链首指针
   }

   void showl(test *head)
   {
   cout<<\"链首指针:\"< <
   while(head)//以内存指向为null为条件循环显示先前输入内容
   {
   cout< name<<\"|\"< socre<
   head=head->next;
   }
   }

   void
   {
   showl(create);
   cin.get;
   cin.get;
   }
   上面代码我们是要达到个目:就是要存储你输入人名和他们得分并且以链状结构把它们组合成个链状结构

种有两个组成部分
   test *create
   和 void showl(test *head)
   这两个create是用来创建链表 showl是用来显示链表

   create返回类型是个结构体指针时候我们用了showl(create);而不用引用原因是引导指针是个全局指针变量我们不能在showl内改变它showl内有个移动操作head=head->next;如果是引用话我们就破坏了head指针位置以至于我们再也无法找会首地址位置了

   下面我们来分解整个个初学者思想来研究整个由浅入深逐步解释

   首先我们写这个要考虑到由于是个链表结构我们不可能知道它大小到底是多大这个问题我们可以用动态开辟堆内存来解决堆内存在结束前始终是有效不受栈空间生命期限制但要注意是我们必须有个指针变量来存储这链状结构进入地址而在内部来建立这指针变量显然是不合适旦退出这个指针变量也随的失效所以我们在开始声明了个全局指针变量

   test *head;//创建个全局引导进入链表指针
   好解决了这两个问题我们接下去研究

   有输入就必然有输出由于输出和输入是相对独立为了不断测试正确性好调试我们先写好输出创建我们先约定好名为create

   我们先写出如下代码:

   #
   using s;

   suct test
   {
   char name[10];
   float socre;
   test *next;
   };

   test *head;//创建个全局引导进入链表指针

   test *create
   {

  
head;//返回链首指针
   }

   void showl(test *head)
   {
   cout<<\"链首指针:\"< <
   while(head)//以内存指向为null为条件循环显示先前输入内容
   {
   cout< name<<\"|\"< socre<


   head=head->next;
   }
   }

   void
   {
   showl(create);
   cin.get;
   cin.get;
   }
   写到这里基本形态已经出来输入和我们已经有了

   下面我们来解决输入问题链表实现我们是通过循环输入来实现既然是循环我们就定得考虑终止循环条件避免死循环和无效循环发生

   在create内部我们先写成这样:

   test *create
   {
   test *ls;//节点指针
   test *le;//链尾指针
   ls = test;//把ls指向动态开辟堆内存地址
   cin>>ls->name>>ls->socre;
   head=NULL;//进入时候先不设置head指针指向任何地址,不知道是否上来就输入null跳出
   le=ls;//把链尾指针设置成刚刚动态开辟堆内存地址,用于等下设置le->next,也就是下个节点位置

   le->next=NULL;//把链尾指针next设置为空,不管如何循环总是要结束,设置为空才能够在循环显链表时候不至于死循环
   delete ls;//当结束时候最后个动态开辟内存是无效,所以必须清除掉
   head;//返回链首指针
   }
   在循环创建的前我们必须考虑个都不输入情况

   单进入create我们首先必然要创建个节点我们先创建个节点指针后把者个节点指针指向到动态开辟test类型动态内存地址位置上

   test *ls;
   ls = test;
   既然是循环输入而结构成员test *next又是用来存储下个接点内存地址每次循环我们又要动态创建个新内存空间所以我们必须要有个指针来存储上次循环动态开辟内存地址于是就有了

   test *le;
   接下来在进入循环前我们要创建链表个节点个节点必然是在循环外创建于是就有了

   cin>>ls->name>>ls->socre;
   执行者情况是位置所以我们必然要考虑上来就不想继续运行情况所以我们开始先把head引导指针设置为不指向任何地址也就是

   head=NULL;

   为了符合le也就是链尾指针设计思路我们在循环前定要保存刚刚动态开辟内存地址好在下次循环时候设置上个节点中next成员指向于是我们便有了:

   le=ls;
   为了实现循环输入我们又了下面代码:

   while(scmp(ls->name,\"null\")!=0)
   {
   (headNULL)
   {
   head=ls;
   }
  
   {
   le->next=ls;
   }
   le=ls;
   ls= test;
   cin>>ls->name>>ls->socre;
   }
   是循环必然要有终止循环条件所以我们循环条件是:

   while(scmp(ls->name,\"null\")!=0)
   输入名字是null时候就停止循环

   为了保证第次进入循环也就是在循环内准备创建第 2个节点前设置引导指针指向我们有了如下判断代码:

   (headNULL)
   {
   head=ls;
   }
  
   {
   le->next=ls;
   }

   代码中条件是为了设置前个节点next指向而写,这点我们记住先看下面代码,稍后大家回过头想就明白了

   le=ls;
   ls= test;
   cin>>ls->name>>ls->socre;
   le=ls;这么写就是为了保存上次循环指针位置而设,正是为了上面代码而做预先保留

   ls= test;
   cin>>ls->name>>ls->socre;
   这两行代码意思就是继续开辟下个节点空间,和输入节点内容!

   循环旦结束也就结束了,为了保持不出错,也就是最后个节点next成员指向为空我们有了下面代码

   le->next=NULL;

   思路始终是以先开辟后判断为思路,所以到最后个不成立时候总会有个多开辟内存空间,为了删除掉它,我们有了下面代码

   delete ls;
   到最后由于返回head指针

   head;
   显示链表没有什么太多特别也只需要注意下面这样就可以了!

   head=head->next;
   我们的所以不用head =1;来写就是链表是我们动态开辟而每个节点位置并不是相连next成员指针意义也就是下个节点内存地址

   到这里整个创建设计思路也都说完了笔者不定说很好但基本思路是这样希望读者多研究多对比相信此教程还是对大家有帮助设计就是利用逐步研究方式进行写好代码往往直接看看不懂就是中间细节并不是次都能够想到

   下面我们来说下链表节点删除!

   我们以上面为基础但为了我们方便学习删除我们休整结构体为

   suct test
   {
   number;
   float socre;
   test *next;
   };
   number为唯编号每个节点

   删除我就不多说了里面重要部分有注解

   特别注意deletel参数意义指针引用在这里很重要如果只是指针或者只是应用都是不行为什么仔细研究很多知名教材在这问题上都很模糊而且很多书还有不错但思路是错我这里特别不说请大家仔细阅读如果还是有问题可以回此帖我会回答

   完整代码如下:

   #
   using s;
   suct test
   {
   number;
   float socre;
   test *next;
   };
   test *head;//创建个全局引导进入链表指针

   test *create
   {
   test *ls;//节点指针
   test *le;//链尾指针
   ls = test;//把ls指向动态开辟堆内存地址


   cin>>ls->number>>ls->socre;
   head=NULL;//进入时候先不设置head指针指向任何地址,不知道是否上来就输入null跳出
   le=ls;//把链尾指针设置成刚刚动态开辟堆内存地址,用于等下设置le->next,也就是下个节点位置
   while(ls->number!=0)//创建循环条件为ls->number值不是null,用于循环添加节点
   {
   (headNULL)//判断是否是第次进入循环
   {
   head=ls;//如果是第次进入循环,那么把引导进入链表指针指向第次动态开辟堆内存地址
   }
  
   {
   le->next=ls;//如果不是第次进入那么就把上链尾指针le->next指向上次循环结束前动态创建堆内存地址
   }
   le=ls;//设置链尾指针为当前循环中节点指针,用于下次进入循环时候把上节点next指向上次循环结束前动态创建堆内存地址
   ls= test;//为下个节点在堆内存中动态开辟空间
   cin>>ls->number>>ls->socre;
   }
   le->next=NULL;//把链尾指针next设置为空,不管如何循环总是要结束,设置为空才能够在循环显链表时候不至于死循环
   delete ls;//当结束时候最后个动态开辟内存是无效,所以必须清除掉
   head;//返回链首指针
   }
   void showl(test *head)
   {
   cout<<\"链首指针:\"< <
   while(head)//以内存指向为null为条件循环显示先前输入内容
   {
   cout< number<<\"|\"< socre<
   head=head->next;
   }
   }
   void deletel(test *&head, number)//这里如果参数换成test *head,意义就完全区别了,head变成了复制而不是原有链上操作了,特别注意,很多书上都不对这里
   {
   test *po;//判断链表是否为空
   (headNULL)
   {
   cout<<\"链表为空,不能进行删除工作!\";
   ;
   }
   (head->numbernumber)//判删除节点是否为首节点
   {
   po=head;
   cout<<\"删除点是链表第个节点位置!\";
   head=head->next;//重新设置引导指针
   delete po;
   ;
   }
   test *fp=head;//保存连首指针
   for(test *&mp=head;mp->next;mp=mp->next)
   {
   (mp->next->numbernumber)
   {
   po=mp->next;
   mp->next=po->next;
   delete po;
   head=fp;//由于head不断移动丢失了head,把进入循环前head指针恢复!
   ;
   }
   }
   }
   void
   {
   head=create;//创建
   showl(head);
   dp;
   cin>>dp;
   deletel(head,dp);//删除
   showl(head);
   cin.get;
   cin.get;
   }

   最后我学习下如何在已有链表上插入节点

   我们要考虑 4中情况,

   1.链表为空!

   2.插入点在首节点前

   3.插入点找不到情况我们设置放在最后!

   4.插入点在中间情况!

   今天在昨天基础上做了进修改,可以避免删除点找不到情况,如果找不到删除点就退出!

   #
   using s;
   suct test
   {
   number;
   float socre;
   test *next;
   };
   test *head;//创建个全局引导进入链表指针

   test *create
   {
   test *ls;//节点指针
   test *le;//链尾指针
   ls = test;//把ls指向动态开辟堆内存地址
   cout<<\"请输入第个节点number和节点score,输入0.0跳出\"<
   cin>>ls->number>>ls->socre;
   head=NULL;//进入时候先不设置head指针指向任何地址,不知道是否上来就输入null跳出
   le=ls;//把链尾指针设置成刚刚动态开辟堆内存地址,用于等下设置le->next,也就是下个节点位置
   while(ls->number!=0)//创建循环条件为ls->number值不是null,用于循环添加节点
   {
   (headNULL)//判断是否是第次进入循环
   {
   head=ls;//如果是第次进入循环,那么把引导进入链表指针指向第次动态开辟堆内存地址
   }
  
   {
   le->next=ls;//如果不是第次进入那么就把上链尾指针le->next指向上次循环结束前动态创建堆内存地址
   }
   le=ls;//设置链尾指针为当前循环中节点指针,用于下次进入循环时候把上节点next指向上次循环结束前动态创建堆内存地址
   ls= test;//为下个节点在堆内存中动态开辟空间
   cout<<\"请下个节点number和节点score,输入0.0跳
Tags:  结构体数组 结构体指针 结构体 结构体中定义结构体

延伸阅读

最新评论

发表评论