c语言结构体指针初始化

***************************************************

更多精彩,欢迎进入:http://shop115376623.taobao.com

***************************************************


记得上周在饭桌上和同事讨论C语言的崛起时,讲到了内存管理方面
我说所有指针使用前都必须初始化,结构体中的成员指针也是一样
有人反驳说,不是吧,以前做二叉树算法时,他的左右孩子指针使用时难道有初始化吗
那时我不知怎么的想不出理由,虽然我还是坚信要初始化的

过了几天这位同事说他试了一下,结构体中的成员指针不经过初始化是可以用(左子树和右子树指针)
那时在忙着整理文档,没在意
今天抽空调了一下,结论是,还是需要初始化的。
而且,不写代码你是不知道原因的(也许是对着电脑久了IQ和记性严重下跌吧)
测试代码如下

 

  1. #include    
  2. #include    
  3. #include    
  4.   
  5. struct student{   
  6.   char *name;   
  7.   int score;   
  8.   struct student* next;   
  9. }stu,*stu1;    
  10.   
  11. int main(){    
  12.   stu.name = (char*)malloc(sizeof(char)); /*1.结构体成员指针需要初始化*/  
  13.   strcpy(stu.name,"Jimy");   
  14.   stu.score = 99;   
  15.   
  16.   stu1 = (struct student*)malloc(sizeof(struct student));/*2.结构体指针需要初始化*/  
  17.   stu1->name = (char*)malloc(sizeof(char));/*3.结构体指针的成员指针同样需要初始化*/  
  18.   stu.next  = stu1;   
  19.   strcpy(stu1->name,"Lucy");   
  20.   stu1->score = 98;   
  21.   stu1->next = NULL;   
  22.   printf("name %s, score %d \n ",stu.name, stu.score);   
  23.   printf("name %s, score %d \n ",stu1->name, stu1->score);   
  24.   free(stu1);   
  25.   return 0;   
  26. }  
#include 
#include 
#include struct student{char *name;int score;struct student* next;
}stu,*stu1; int main(){     stu.name = (char*)malloc(sizeof(char)); /*1.结构体成员指针需要初始化*/strcpy(stu.name,"Jimy");stu.score = 99;stu1 = (struct student*)malloc(sizeof(struct
student));/*2.结构体指针需要初始化*/stu1->name =
(char*)malloc(sizeof(char));/*3.结构体指针的成员指针同样需要初始化*/stu.next  = stu1;strcpy(stu1->name,"Lucy");stu1->score = 98;stu1->next = NULL;printf("name %s, score %d \n ",stu.name, stu.score);printf("name %s, score %d \n ",stu1->name, stu1->score);free(stu1);return 0;
}



写测试代码的过程中我明白了,同事所说的二叉树遍历算法中所用的左子树和右子树指针不需要初始化,其实是这样的,左子树和右子树指向的必须是二叉树节点类型的结构体指针(你填一个长度相同的指针也可以),而该结构体指针是需要初始化的(见注释2),也就是并没有通过malloc来分配内存,而是将另一个指针的值赋给它

顿时觉得挺无语的,确实,看了很多大学里的教材,对于二叉树的遍历等算法定义的结构体无非是以下形式
 

  1. struct node{   
  2.   int data;   
  3.   struct node* lchild, rchild;   
  4. };  
struct node{int data;struct node* lchild, rchild;
};


使用时都直接的
 

  1. struct node* root;   
  2.  root = (struct node*)malloc(sizeof(struct node));   
  3.  root->data = 3;   
  4.   
  5.  struct node* nlchild;   
  6.  nlchild = (struct node*)malloc(sizeof(struct node));   
  7.  root->lchild = nlchild;   
  8.  nlchild->data = 2;    
  9.   
  10.  struct node* nrchild;   
  11.  nlrchild = (struct node*)malloc(sizeof(struct node));   
  12.  root->rchild = nrchild;   
  13.  nrchild->data = 4;   
 struct node* root;root = (struct node*)malloc(sizeof(struct node));root->data = 3;struct node* nlchild;nlchild = (struct node*)malloc(sizeof(struct node));root->lchild = nlchild;nlchild->data = 2; struct node* nrchild;nlrchild = (struct node*)malloc(sizeof(struct node));root->rchild = nrchild;nrchild->data = 4; 


这样子给人造成一种错觉好像结构体成员指针是不用初始化的。

可是,只要是指针,要使用它前就必须保证指针变量的值是一个有效的值;否则,它指向的内存一定是垃圾数据!
C语言的内存管理很重要,集魄力和麻烦于一身,看你自己的心态如何了。如果你积极的面对,你正在控制一切;如果你觉得烦躁,你正不得不控制一切。C仍旧是博大精深的语言,信C哥!

/*附加:仍旧是指针*/
 

  1. stu1 = (struct student*)malloc(sizeof(struct student));/*2.结构体指针需要初始化*/  
  stu1 = (struct student*)malloc(sizeof(struct
student));/*2.结构体指针需要初始化*/


这一句可能会有人把sizeof里边也填成struct student*
可以理解这样的行为,因为stu本来就是struct student*,可是这样子你就没有为结构体分配足够的内存,使用中会因为内存错误同样报错的。
当然,仅仅为结构体指针分配内存还不够,结构体成员指针仍然需要分配内存,如下
 

  1. stu1->name = (char*)malloc(sizeof(char));  

 

 

 

 

 

 

 

 

 

 

自己在用结构体指针的时候遇到的引用问题,网上找的一段文字觉得挺不错的,可能对大家有帮助。

在使用结构体指针变量的时候,往往容易犯一个“低级”错误。即定义一个结构体指针变量后就直接对结构体指针变量所指向的结构体成员进行操作,从而产生一些莫名其妙的错误。我们必须要给结构体指针变量赋予一个有效的结构体变量地址,才能正常操作结构体指针变量。比如:

struct UART{

             int a;

             uchar b;

          }

main()

{

      struct UART  *p;

      p->a = 0xXXX;

      p->b = 0xXX;

     printf("%i,%c",p->b,p->a);

}

这个程序输出的值将是不可预知的,因为“在程序中只是定义了一个结构体指针变量,并没有给该结构体指针变量赋一个有效值,因此该结构体变量所指向的地址将不确定,从而不能得到预期结果”

应该改为:

struct UART{

             int a;

             uchar b;

       }

main()

{

      struct UART  *p;

     struct UART dd;

      p = ⅆ               //这句一定要有,否则将出现不可预知的问题

      p->a = 0xXXX;

      p->b = 0xXX;

     printf("%i,%c",p->b,p->a);

}

 

 

C/C++中

 

结构体(struct)知识点强化 为了进一部的学习结构体这一重要的知识点,我们今天来学习一下链表结构。

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

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

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

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

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

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

   #include
   using namespace std;

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

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

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

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

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

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

   void main()
   {
   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;//创建一个全局的引导进入链表的指针
   好解决了这两个问题,我们接下去思考

  有输入就必然有输出,由于输出函数和输入函数是相对独立的,为了不断测试程序的正确性好调试我们先写好输出函数和main函数捏的调用,创建函数我们先约定好名为create。

   我们先写出如下的代码:

   #include
   using namespace std;

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

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

   test *create()
   {

   return head;//返回链首指针
   }

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

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

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

   在create()函数内部我们先写成这样:

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

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

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

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

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

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

   head=NULL;

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

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

本文来自: 站长(http://www.qqcf.com) 详细出处参考:http://study.qqcf.com/web/171/19838.htm

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458874.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WWDC 2013 Session笔记 - iOS7中弹簧式列表的制作

这是我的WWDC2013系列笔记中的一篇&#xff0c;完整的笔记列表请参看这篇总览。本文仅作为个人记录使用&#xff0c;也欢迎在许可协议范围内转载或使用&#xff0c;但是还烦请保留原文链接&#xff0c;谢谢您的理解合作。如果您觉得本站对您能有帮助&#xff0c;您可以使用RSS或…

H5学习之旅-H5列表(8)

列表的基本语法 ol&#xff1a;有序列表 ul&#xff1a;无序列表 li&#xff1a;列表项 dl&#xff1a;列表 dt&#xff1a;列表项 dd&#xff1a;列表描述 常用列表 1.无序列表&#xff1a;使用标签 ul&#xff0c;li 属性&#xff1a;disc&#xff08;默认实心圆&#xff09;…

c语言 文件夹是否纯真,C语言面试题大汇总之华为面试题(转)

1、局部变量能否和全局变量重名&#xff1f;答&#xff1a;能&#xff0c;局部会屏蔽全局。要用全局变量&#xff0c;需要使用 ":: "局部变量可以与全局变量同名&#xff0c;在函数内引用这个变量时&#xff0c;会用到同名的局部变量&#xff0c;而不会用到全局变量。…

virtual析构函数的作用?

*************************************************** 更多精彩&#xff0c;欢迎进入&#xff1a;http://shop115376623.taobao.com *************************************************** 大家知道&#xff0c;析构函数是为了在对象不被使用之后释放它的资源&#xff0c;虚函…

[codevs1039]数的划分

这一题实际上是组合数学里面的经典问题&#xff0c;跟第二类Stirling数有些相似。可以把一个数值为n的数看成n个小球&#xff0c;划分的份数k看作是k个盒子&#xff0c;那么本题的要求就是&#xff1a; 将n个小球放到k个盒子中&#xff0c;小球之间与盒子之间没有区别&#xff…

c语言中用文件处理数据,C语言文件处理 -C语言从文件中读写格式化数据

从图 1 所示的文件 fin.txt 中读取学生姓名、身高和体重&#xff0c;计算并显示它们的平均值&#xff0c;并且将显示结果保存到文件 fout.txt 中。图 1&#xff1a;输入输出文件示例C语言代码清单 1&#xff1a;读取学生姓名、身高和体重&#xff0c;计算并显示它们的平均值#in…

MySQL于ON DUPLICATE KEY UPDATE采用

今天我们做的推断插入用途MySQL于ON DUPLICATE KEY UPDATE。现在&#xff0c;Mark下面&#xff01;假设你想做的事&#xff0c;再有就是在数据库中插入数据没有数据、如果有数据更新数据&#xff0c;然后你可以选择ON DUPLICATE KEY UPDATE。 ON DUPLICATE KEY UPDATE可以在UNI…

C++中Static作用和使用方法

*************************************************** 更多精彩&#xff0c;欢迎进入&#xff1a;http://shop115376623.taobao.com *************************************************** 1、什么是static? static 是C中很常用的修饰符&#xff0c;它被用来控制变量的…

三星+android+7.0+自动纠正单词,升级党必看!三星S/Note系列更新Android 7.0指南

上周&#xff0c;雷科技(微信ID&#xff1a;leitech)曾为大家提供了一份Android 7.0升级预测名单。现在为了增加针对性&#xff0c;这次笔者就以三星最热门的两大旗舰S和Note系列为例&#xff0c;给大家提供一个全方面的更新指南。截至目前&#xff0c;三星S系列和Note系列的正…

iOS开发出错whose view is not in the window hierarchy!的解决

大熊猫猪侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 一个简单的单窗口App在运行时出现错误: 2016-04-07 14:28:48.411 BlurViewAndPopView[4364:168520] Warning: Attempt to present <UIAlertCon…

互联网年鉴

最近互联网行业&#xff0c;有一个词说的比较多了&#xff0c;“资本寒冬”。作为&#xff0c;一个在这个行业里干的人&#xff0c;真心是好怕怕呢。 妈蛋&#xff0c;真怕哪天就突然失业了呀。所以&#xff0c;无聊的(操蛋的)去整理了一些98年开始一直到现在的互联网中一些自己…

android oreo 开机动画,Android O添加了一个 Splash Screen API帮助简化启动屏制作

大多数开发者对闪屏(splash screen)都持不同的态度。一些人支持用闪屏隐藏app的加载&#xff0c;然后自然过渡到主界面&#xff0c;而另一些人认为闪屏不管是对用户还是开发者都是浪费时间。谷歌在这个问题上的立场也是不一的&#xff0c;以前不推荐使用闪屏的使用&#xff0c;…

C++空类默认函数

*************************************************** 更多精彩&#xff0c;欢迎进入&#xff1a;http://shop115376623.taobao.com *************************************************** 定义一个空的C类&#xff0c;例如 class Empty { } 一个空的class在C编译器处理过后就…

拼接SQL的一个技巧

2019独角兽企业重金招聘Python工程师标准>>> 为了避免判断是否要在字符串中加 AND 来连接查询条件&#xff0c;我们可以直接在在WHERE 后面写 id>0。 这样&#xff0c;我们在拼接条件的时候&#xff0c;就可以直接写 AND 了。 SELECT* FROM t_table WHEREid>0…

【Linux导论】Linux引导流程(The Boot Process)

原文 LFS101x.2 Introduction to Linux (Linux Foundation) Chapter 03: Linux Structure and Installation - Section 2: The Boot Process 引导流程 - (The Boot Process) 你是否想过&#xff0c;在你按下电源开关直至Linux登陆提示出现时&#xff0c;后台到底发生了哪些事&a…

String类 写出类的成员函数实现

*************************************************** 更多精彩&#xff0c;欢迎进入&#xff1a;http://shop115376623.taobao.com *************************************************** 已知String类定义如下&#xff1a; class String { public: String(const char *str …

android 管理类,android 线程管理类

1. 延时任务以及取消延时的应用场景&#xff1a;实时搜索&#xff0c;文字内容变化时候不发送请求&#xff0c;当停止变化指定时间后再发送请求2. 使用方法&#xff1a;// 回到主线程MainThreadHelper.runOnUIthread(new Runnable() {Overridepublic void run() {// pushActivi…

计算一行文本的高度

计算一行文本的高度 说明 有时候我们需要知道指定的几行文本的高度&#xff0c;此工具用于解决此种问题。 源码 // // NSStringLabelWidthAndHeight.h // ZiPeiYi // // Created by YouXianMing on 15/12/9. // Copyright © 2015年 YouXianMing. All rights reserved.…

python笔记-python编程优化:常用原则和技术介绍

本人翻译自《Exper Python Programming》 Premature optimization is the root of all evil in programming -Donald Knuth 优化的三原则 让它跑起来先一个非常常见的错误就是在编写代码之初我们就开始对代码进行优化。让人伤心的是这通常做的都是无用功&#xff0c;很多软…

C++成员函数重载、覆盖和隐藏的区别

*************************************************** 更多精彩&#xff0c;欢迎进入&#xff1a;http://shop115376623.taobao.com *************************************************** C成员函数重载、覆盖和隐藏的区别class Base{public:void Walk(int x){ cout <<…