c语言入门:C语言入门之结构(3)



 结构指针变量作参数

  在ANSIC标准中允许用结构变量作参数进行整体传送但是这种传送要将全部成员逐个传送特别是成员为时将会使传送时间和空间开销很大严重地降低了效率因此最好办法就是使用指针即用指针变量作参数进行传送这时由实参传向形参只是地址从而减少了时间和空间开销

  [例7.8]题目和例7.4相同计算组学生平均成绩和不及格人数

  用结构指针变量作参数编程

structstu
{
 num;
 char*name;
 charsex;
 floatscore;}boy[5]={
  {101,"Liping",’M’,45},
  {102,"Zhangping",’M’,62.5},
  {103,"Hefang",’F’,92.5},
  {104,"Chengling",’F’,87},
  {105,"Wangming",’M’,58},
 };

{
 structstu*ps;
 voidave(structstu*ps);
 ps=boy;
 ave(ps);
}
voidave(structstu*ps)
{
 c=0,i;
 floatave,s=0;
 for(i=0;i<5;i,ps)
 {
  s=ps->score;
  (ps->score<60)c=1;
 }
 prf("s=%f\\n",s);
 ave=s/5;
 prf("average=%f\\ncount=%d\\n",ave,c);
}

  本中定义了ave其形参为结构指针变量psboy被定义为外部结构因此在整个源中有效中定义介绍说明了结构指针变量ps并把boy首地址赋予它使ps指向boy然后以ps作实参aveave中完成计算平均成绩和统计不及格人数工作并输出结果和例7.4相比由于本全部采用指针变量作运算和处理故速度更快效率更高.

  topoic=动态存储分配

  在章中曾介绍过长度是预先定义好在整个中固定不变C语言中不允许动态类型例如:n;scanf("%d",&n);a[n];用变量表示长度想对大小作动态介绍说明这是但是在实际编程中往往会发生这种情况即所需内存空间取决于实际输入数据而无法预先确定对于这种问题办法很难解决为了解决上述问题C语言提供了些内存管理这些内存管理可以按需要动态地分配内存空间也可把不再使用空间回收待用为有效地利用内存资源提供了手段常用内存管理有以下 3个:

  1.分配内存空间malloc

  形式:(类型介绍说明符*)malloc(size)功能:在内存动态存储区中分配块长度为"size"字节连续区域返回值为该区域首地址“类型介绍说明符”表示把该区域用于何种数据类型(类型介绍说明符*)表示把返回值强制转换为该类型指针“size”是个无符号数例如:pc=(char*)malloc(100);表示分配100个字节内存空间并强制转换为类型返回值为指向该指针把该指针赋予指针变量pc

  2.分配内存空间calloc

  calloc也用于分配内存空间形式:(类型介绍说明符*)calloc(n,size)功能:在内存动态存储区中分配n块长度为“size”字节连续区域返回值为该区域首地址(类型介绍说明符*)用于强制类型转换calloc和malloc区别仅在于次可以分配n块区域例如:ps=(struetstu*)calloc(2,(structstu));其中(structstu)是求stu结构长度因此该语句意思是:按stu长度分配2块连续区域强制转换为stu类型并把其首地址赋予指针变量ps

  3.释放内存空间free

  形式:free(void*ptr);功能:释放ptr所指向块内存空间ptr是个任意类型指针变量它指向被释放区域首地址被释放区应是由malloc或calloc所分配区域:[例7.9]分配块区域输入个学生数据


{
 structstu
 {
  num;
  char*name;
  charsex;
  floatscore;
 }*ps;
 ps=(structstu*)malloc((structstu));
 ps->num=102;
 ps->name="Zhangping";
 ps->sex=’M’;
 ps->score=62.5;
 prf("Number=%d\\nName=%s\\n",ps->num,ps->name);
 prf("Sex=%c\\nScore=%f\\n",ps->sex,ps->score);
 free(ps);
}

  本例中定义了结构stu定义了stu类型指针变量ps然后分配块stu大内存区并把首地址赋予ps使ps指向该区域再以ps为指向结构指针变量对各成员赋值并用prf输出各成员值最后用free释放ps指向内存空间整个包含了申请内存空间、使用内存空间、释放内存空间 3个步骤实现存储空间动态分配链表概念在例7.9中采用了动态分配办法为个结构分配内存空间次分配块空间可用来存放个学生数据我们可称的为个结点有多少个学生就应该申请分配多少块内存空间也就是说要建立多少个结点当然用结构也可以完成上述工作但如果预先不能准确把握学生人数也就无法确定大小而且当学生留级、退学的后也不能把该元素占用空间从中释放出来用动态存储思路方法可以很好地解决这些问题个学生就分配个结点无须预先确定学生准确人数某学生退学可删去该结点并释放该结点占用存储空间从而节约了宝贵内存资源方面思路方法必须占用块连续内存区域而使用动态分配时每个结点的间可以是不连续(结点内是连续)结点的间联系可以用指针实现即在结点结构中定义个成员项用来存放下结点首地址这个用于存放地址成员常把它称为指针域可在第个结点指针域内存入第 2个结点首地址在第 2个结


指针域内又存放第 3个结点首地址如此串连下去直到最后个结点最后个结点因无后续结点连接其指针域可赋为0这样种连接方式在数据结构中称为“链表”

  在链表中第0个结点称为头结点它存放有第个结点首地址它没有数据只是个指针变量以下每个结点都分为两个域个是数据域存放各种实际数据如学号num姓名name性别sex和成绩score等个域为指针域存放下结点首地址链表中个结点都是同种结构类型例如个存放学生学号和成绩结点应为以下结构:

structstu
{
 num;
 score;
 structstu*next;
}




  前两个成员项组成数据域个成员项next构成指针域它是个指向stu类型结构指针变量链表基本操作对链表主要操作有以下几种:

  1.建立链表;

  2.结构查找和输出;

  3.插入个结点;

  4.删除个结点;

  下面通过例题来介绍说明这些操作

  [例7.10]建立个 3个结点链表存放学生数据为简单起见我们假定学生数据结构中只有学号和年龄两项

  可编写个建立链表creat如下:

#NULL0
#TYPEstructstu
#LEN(structstu)
structstu
{
 num;
 age;
 structstu*next;
};
TYPE*creat(n)
{
 structstu*head,*pf,*pb;
 i;
 for(i=0;i<n;i)
 {
  pb=(TYPE*)malloc(LEN);
  prf("inputNumberandAge\\n");
  scanf("%d%d",&pb->num,&pb->age);
  (i0)
   pf=head=pb;
  pf->next=pb;
  pb->next=NULL;
  pf=pb;
 }
 (head);
}

  在外首先用宏定义对 3个符号常量作了定义这里用TYPE表示structstu用LEN表示(structstu)主要是为了在以下内减少书写并使阅读更加方便结构stu定义为外部类型各个均可使用该定义

  creat用于建立个有n个结点链表它是个指针它返回指针指向stu结构在creat内定义了 3个stu结构指针变量head为头指针pf为指向两相邻结点结点指针变量pb为后结点指针变量在for语句内用malloc建立长度和stu长度相等空间作为结点首地址赋予pb然后输入结点数据如果当前结点为第结点(i0)则把pb值(该结点指针)赋予head和pf如非第结点则把pb值赋予pf所指结点指针域成员next而pb所指结点为当前最后结点其指针域赋NULL再把pb值赋予pf以作下次循环准备

  creat形参n表示所建链表结点数作为for语句循环次数图7.4表示了creat执行过程

  [例7.11]写在链表中按学号查找该结点

TYPE*search(TYPE*head,n)
{
 TYPE*p;
 i;
 p=head;
 while(p->num!=n&&p->next!=NULL)
  p=p->next;/*不是要找结点后移步*/
  (p->numn)(p);
  (p->num!=n&&p->nextNULL)
  prf("Node%dhasnotbeenfound!\\n",n
}

  本中使用符号常量TYPE和例7.10宏定义相同等于struct stu有两个形参head是指向链表指针变量n为要查找学号进入while语句逐个检查结点num成员是否等于n如果不等于n且指针域不等于NULL(不是最后结点)则后移个结点继续循环如找到该结点则返回结点指针如循环结束仍未找到该结点则输出“未找到”提示信息

  [例7.12]写删除链表中指定结点删除个结点有两种情况:

  1.被删除结点是第个结点这种情况只需使head指向第 2个结点即可即head=pb->next其过程如图7.5所示

  2.被删结点不是第个结点这种情况使被删结点结点指向被删结点结点即可即pf->next=pb->next

  编程如下:

TYPE*delete(TYPE*head,num)
{
 TYPE*pf,*pb;
 (headNULL)/*如为空表输出提示信息*/
 {
  prf("\\nemptylist!\\n");
  gotoend;
 }
 pb=head;
 while(pb->num!=num&&pb->next!=NULL)
  /*当不是要删除结点而且也不是最后个结点时继续循环*/
 {
  pf=pb;pb=pb->next;}/*pf指向当前结点pb指向下结点*/
  (pb->numnum)


  {
   (pbhead)head=pb->next;
    /*如找到被删结点且为第结点则使head指向第 2个结点
     否则使pf所指结点指针指向下结点*/
   pf->next=pb->next;
   free(pb);
   prf("Thenodeisdeleted\\n");}
  
   prf("Thenodenotbeenfoud!\\n");
   end:
  head;
 }

  有两个形参head为指向链表第结点指针变量num删结点学号首先判断链表是否为空为空则不可能有被删结点若不为空则使pb指针指向链表个结点进入while语句后逐个查找被删结点找到被删结点的后再看是否为第结点若是则使head指向第 2结点(即把第结点从链中删去)否则使被删结点结点(pf所指)指向被删结点结点(被删结点指针域所指)如若循环结束未找到要删结点则输出“末找到”提示信息最后返回head值

  [例7.13]写在链表中指定位置插入个结点个链表指定位置插入结点要求链表本身必须是已按某种规律排好序例如在学生数据链表中要求学号顺序插入个结点设被插结点指针为pi可在 3种区别情况下插入

  1.原表是空表只需使head指向被插结点即可

  2.被插结点值最小应插入第结点的前这种情况下使head指向被插结点被插结点指针域指向原来结点则可即:

pi->next=pb;
head=pi;

  3.在其它位置插入这种情况下使插入位置结点指针域指向被插结点使被插结点指针域指向插入位置结点即为:pi->next=pb;pf->next=pi;

  4.在表末插入这种情况下使原表末结点指针域指向被插结点被插结点指针域置为NULL即:

pb->next=pi;
pi->next=NULL;TYPE*insert(TYPE*head,TYPE*pi)
{
 TYPE*pf,*pb;
 pb=head;
 (headNULL)/*空表插入*/
  (head=pi;
  pi->next=NULL;}
 
 {
  while((pi->num>pb->num)&&(pb->next!=NULL))
  {
   pf=pb;
   pb=pb->next;
  }/*找插入位置*/
  (pi->num<=pb->num)
  {
   (headpb)head=pi;/*在第结点的前插入*/
   pf->next=pi;/*在其它位置插入*/
   pi->next=pb;}
  
  {
   pb->next=pi;
   pi->next=NULL;
  }/*在表末插入*/
 }
 head;
}


  本有两个形参均为指针变量head指向链表pi指向被插结点中首先判断链表是否为空为空则使head指向被插结点表若不空则用while语句循环查找插入位置找到的后再判断是否在第结点的前插入若是则使head指向被插结点被插结点指针域指向原第结点否则在其它位置插入若插入结点大于表中所有结点则在表末插入返回个指针是链表头指针当插入位置在第个结点的前时插入新结点成为链表个结点因此head值也有了改变故需要把这个指针返回主调




  [例7.14]将以上建立链表删除结点插入结点组织在再建个输出全部结点然后用它们

#NULL0
#TYPEstructstu
#LEN(structstu)
structstu
{
 num;
 age;
 structstu*next;
};
TYPE*creat(n)
{
 structstu*head,*pf,*pb;
 i;
 for(i=0;i<n;i)
 {
  pb=(TYPE*)malloc(LEN);
  prf("inputNumberandAge\\n");
  scanf("%d%d",&pb->num,&pb->age);
  (i0)
   pf=head=pb;
  pf->next=pb;
  pb->next=NULL;
  pf=pb;
 }
 (head);
}
TYPE*delete(TYPE*head,num)
{
 TYPE*pf,*pb;
 (headNULL)
 {
  prf("\\nemptylist!\\n");
  gotoend;
 }
 pb=head;
 while(pb->num!=num&&pb->next!=NULL)
 {
  pf=pb;pb=pb->next;
 }
 (pb->numnum)
 {
  (pbhead)head=pb->next;
  pf->next=pb->next;


  prf("Thenodeisdeleted\\n");
 }
 
  free(pb);
  prf("Thenodenotbeenfound!\\n");
 end:
 head;
}
TYPE*insert(TYPE*head,TYPE*pi)
{
 TYPE*pb,*pf;
 pb=head;
 (headNULL)
 {
  head=pi;
  pi->next=NULL;
 }
 
 {
  while((pi->num>pb->num)&&(pb->next!=NULL))
  {
   pf=pb;
   pb=pb->next;
  }
  (pi->num<=pb->num)
  {
   (headpb)head=pi;
   pf->next=pi;
   pi->next=pb;
  }
  
  {
   pb->next=pi;
   pi->next=NULL;
  }
 }
 head;
}
voidpr(TYPE*head)
{
 prf("Number\\t\\tAge\\n");
 while(head!=NULL)
 {
  prf("%d\\t\\t%d\\n",head->num,head->age);
  head=head->next;
 }
}

{
 TYPE*head,*pnum;
 n,num;
 prf("inputnumberofnode:");
 scanf("%d",&n);
 head=creat(n);
 pr(head);
 prf("Inputthedeletednumber:");
 scanf("%d",&num);
 head=delete(head,num);
 pr(head);
 prf("Inputtheinsertednumberandage:");
 pnum=(TYPE*)malloc(LEN);
 scanf("%d%d",&pnum->num,&pnum->age);
 head=insert(head,pnum);
 pr(head);
}

  本例中pr用于输出链表中各个结点数据域值形参head初值指向链表第个结点在while语句中输出结点值后head值被改变指向下结点若保留头指针head则应另设个指针变量把head值赋予它再用它来替代headn为建立结点数目num为待删结点数据域值;head为指向链表头指针pnum为指向待插结点指针中各行意义是:

  第 6行输入所建链表结点数;

  第 7行调creat建立链表并把头指针返回给head;

  第 8行调pr输出链表;

  第十行输入待删结点学号;

  第十行调delete删除
Tags:  单片机c语言入门 c语言入门经典 c语言入门教程 c语言入门

延伸阅读

最新评论

发表评论