⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 7_结构与联合.txt

📁 一些c的经典案例
💻 TXT
📖 第 1 页 / 共 3 页
字号:
      char *name;
      char sex;
      float score;
    } boy1={102,"Zhang ping",'M',78.5},*pstu;
main()
{
    pstu=&boy1;
    printf("Number=%d\nName=%s\n",boy1.num,boy1.name);
    printf("Sex=%c\nScore=%f\n\n",boy1.sex,boy1.score);
    printf("Number=%d\nName=%s\n",(*pstu).num,(*pstu).name);
    printf("Sex=%c\nScore=%f\n\n",(*pstu).sex,(*pstu).score);
    printf("Number=%d\nName=%s\n",pstu->num,pstu->name);
    printf("Sex=%c\nScore=%f\n\n",pstu->sex,pstu->score);
}
    本例程序定义了一个结构stu,定义了stu类型结构变量boy1 并
作了初始化赋值,还定义了一个指向stu类型结构的指针变量pstu。
    在main函数中,pstu被赋予boy1的地址,因此pstu指向boy1 。
然后在printf语句内用三种形式输出boy1的各个成员值。 从运行结
果可以看出:    结构变量.成员名
    (*结构指针变量).成员名
    结构指针变量->成员名    这三种用于表示结构成员的形式是完全等效的。结构数组指针变量
    结构指针变量可以指向一个结构数组, 这时结构指针变量的值
是整个结构数组的首地址。 结构指针变量也可指向结构数组的一个
元素,这时结构指针变量的值是该结构数组元素的首地址。
    设ps为指向结构数组的指针变量,则ps也指向该结构数组的0号
元素,ps+1指向1号元素,ps+i则指向i号元素。 这与普通数组的情
况是一致的。[例7.7]用指针变量输出结构数组。struct stu
{
    int num;
    char *name;
    char sex;
    float score;
}boy[5]={
          {101,"Zhou ping",'M',45},
          {102,"Zhang ping",'M',62.5},
          {103,"Liou fang",'F',92.5},
          {104,"Cheng ling",'F',87},
          {105,"Wang ming",'M',58},
        };
main()
{
 struct stu *ps;
 printf("No\tName\t\t\tSex\tScore\t\n");
 for(ps=boy;ps<boy+5;ps++)
 printf("%d\t%s\t\t%c\t%f\t\n",ps->num,ps->name,ps->sex,ps->
        score);
}
    在程序中,定义了stu结构类型的外部数组boy 并作了初始化赋
值。在main函数内定义ps为指向stu类型的指针。在循环语句for 的
表达式1中,ps被赋予boy的首地址,然后循环5次,输出boy 数组中
各成员值。    应该注意的是, 一个结构指针变量虽然可以用来访问结构变量
或结构数组元素的成员,但是,不能使它指向一个成员。 也就是说
不允许取一个成员的地址来赋予它。因此,下面的赋值是错误的。    ps=&boy[1].sex;而只能是:
    ps=boy;(赋予数组首地址)
或者是:
    ps=&boy[0];(赋予0号元素首地址)结构指针变量作函数参数
    在ANSI C标准中允许用结构变量作函数参数进行整体传送。 但
是这种传送要将全部成员逐个传送, 特别是成员为数组时将会使传
送的时间和空间开销很大,严重地降低了程序的效率。 因此最好的
办法就是使用指针,即用指针变量作函数参数进行传送。 这时由实
参传向形参的只是地址,从而减少了时间和空间的开销。[例7.8]题目与例7.4相同,计算一组学生的平均成绩和不及格人数。
       用结构指针变量作函数参数编程。struct stu
{
    int num;
    char *name;
    char sex;
    float score;}boy[5]={
        {101,"Li ping",'M',45},
        {102,"Zhang ping",'M',62.5},
        {103,"He fang",'F',92.5},
        {104,"Cheng ling",'F',87},
        {105,"Wang ming",'M',58},
      };
main()
{
    struct stu *ps;
    void ave(struct stu *ps);
    ps=boy;
    ave(ps);
}
void ave(struct stu *ps)
{
    int c=0,i;
    float ave,s=0;
    for(i=0;i<5;i++,ps++)
      {
        s+=ps->score;
        if(ps->score<60) c+=1;
      }
    printf("s=%f\n",s);
    ave=s/5;
    printf("average=%f\ncount=%d\n",ave,c);
}
    本程序中定义了函数ave,其形参为结构指针变量ps。boy 被定
义为外部结构数组,因此在整个源程序中有效。在main 函数中定义
说明了结构指针变量ps,并把boy的首地址赋予它,使ps指向boy 数
组。然后以ps作实参调用函数ave。在函数ave 中完成计算平均成绩
和统计不及格人数的工作并输出结果。
    与例7.4程序相比,由于本程序全部采用指针变量作运算和处理,
故速度更快,程序效率更高。.topoic=动态存储分配
    在数组一章中,曾介绍过数组的长度是预先定义好的, 在整个
程序中固定不变。C语言中不允许动态数组类型。例如:    int n;
    scanf("%d",&n);
    int a[n];    用变量表示长度,想对数组的大小作动态说明, 这是错误的。
但是在实际的编程中,往往会发生这种情况, 即所需的内存空间取
决于实际输入的数据,而无法预先确定。对于这种问题, 用数组的
办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,
这些内存管理函数可以按需要动态地分配内存空间, 也可把不再使
用的空间回收待用,为有效地利用内存资源提供了手段。 常用的内
存管理函数有以下三个:
1.分配内存空间函数malloc调用形式:  (类型说明符*) malloc (size)  功能:在内存的动态存储区中分配一块长度为"size" 字节的连续区
      域。函数的返回值为该区域的首地址。 “类型说明符”表示
      把该区域用于何种数据类型。(类型说明符*)表示把返回值强
      制转换为该类型指针。“size”是一个无符号数。例如:          pc=(char *) malloc (100);      表示分配100个字节的内存空间,并强制转换为字符数组类型,
      函数的返回值为指向该字符数组的指针, 把该指针赋予指针
      变量pc。
2.分配内存空间函数 calloc
calloc 也用于分配内存空间。调用形式:  (类型说明符*)calloc(n,size)  功能:在内存动态存储区中分配n块长度为“size”字节的连续区域。
      函数的返回值为该区域的首地址。(类型说明符*)用于强制类
      型转换。calloc函数与malloc 函数的区别仅在于一次可以分
      配n块区域。例如:          ps=(struet stu*) calloc(2,sizeof (struct stu));      其中的sizeof(struct stu)是求stu的结构长度。因此该语句
      的意思是:按stu的长度分配2块连续区域,强制转换为stu类
      型,并把其首地址赋予指针变量ps。
3.释放内存空间函数free调用形式:  free(void*ptr);  功能:释放ptr所指向的一块内存空间,ptr 是一个任意类型的指针
      变量,它指向被释放区域的首地址。被释放区应是由malloc或
      calloc函数所分配的区域:[例7.9]分配一块区域,输入一个学生数据。main()
{
    struct stu
    {
      int num;
      char *name;
      char sex;
      float score;
    }  *ps;
    ps=(struct stu*)malloc(sizeof(struct stu));
    ps->num=102;
    ps->name="Zhang ping";
    ps->sex='M';
    ps->score=62.5;
    printf("Number=%d\nName=%s\n",ps->num,ps->name);
    printf("Sex=%c\nScore=%f\n",ps->sex,ps->score);
    free(ps);
}
    本例中,定义了结构stu,定义了stu类型指针变量ps。 然后分
配一块stu大内存区,并把首地址赋予ps,使ps指向该区域。再以ps
为指向结构的指针变量对各成员赋值,并用printf 输出各成员值。
最后用free函数释放ps指向的内存空间。 整个程序包含了申请内存
空间、使用内存空间、释放内存空间三个步骤, 实现存储空间的动
态分配。链表的概念
    在例7.9中采用了动态分配的办法为一个结构分配内存空间。每
一次分配一块空间可用来存放一个学生的数据, 我们可称之为一个
结点。有多少个学生就应该申请分配多少块内存空间, 也就是说要
建立多少个结点。当然用结构数组也可以完成上述工作, 但如果预
先不能准确把握学生人数,也就无法确定数组大小。 而且当学生留
级、退学之后也不能把该元素占用的空间从数组中释放出来。 用动
态存储的方法可以很好地解决这些问题。 有一个学生就分配一个结
点,无须预先确定学生的准确人数,某学生退学, 可删去该结点,
并释放该结点占用的存储空间。从而节约了宝贵的内存资源。 另一
方面,用数组的方法必须占用一块连续的内存区域。 而使用动态分
配时,每个结点之间可以是不连续的(结点内是连续的)。 结点之间
的联系可以用指针实现。 即在结点结构中定义一个成员项用来存放
下一结点的首地址,这个用于存放地址的成员,常把它称为指针域。
    可在第一个结点的指针域内存入第二个结点的首地址, 在第二
个结点的指针域内又存放第三个结点的首地址, 如此串连下去直到
最后一个结点。最后一个结点因无后续结点连接,其指针域可赋为0。
这样一种连接方式,在数据结构中称为“链表”。图7.3为链表的示
意图。
    在图7.3中,第0个结点称为头结点, 它存放有第一个结点的首
地址,它没有数据,只是一个指针变量。 以下的每个结点都分为两
个域,一个是数据域,存放各种实际的数据,如学号num,姓名name,
性别sex和成绩score等。另一个域为指针域, 存放下一结点的首地
址。链表中的每一个结点都是同一种结构类型。例如, 一个存放学
生学号和成绩的结点应为以下结构:
    struct stu
    { int num;
      int score;
      struct stu *next;
    }    前两个成员项组成数据域,后一个成员项next构成指针域, 它
是一个指向stu类型结构的指针变量。链表的基本操作
对链表的主要操作有以下几种:    1.建立链表;
    2.结构的查找与输出;
    3.插入一个结点;
    4.删除一个结点;下面通过例题来说明这些操作。[例7.10]建立一个三个结点的链表,存放学生数据。
    为简单起见, 我们假定学生数据结构中只有学号和年龄两项。
可编写一个建立链表的函数creat。程序如下:
    #define NULL 0
    #define TYPE struct stu
    #define LEN sizeof (struct stu)
    struct stu
        {
          int num;
          int age;
          struct stu *next;
        };
    TYPE *creat(int n)
    {
        struct stu *head,*pf,*pb;
        int i;
        for(i=0;i<n;i++)
        {    
          pb=(TYPE*) malloc(LEN);
          printf("input Number and  Age\n");
          scanf("%d%d",&pb->num,&pb->age);
          if(i==0)
          pf=head=pb;
          else pf->next=pb;
          pb->next=NULL;
          pf=pb;
        }
        return(head);
    }
    在函数外首先用宏定义对三个符号常量作了定义。这里用 TYPE
表示struct stu,用LEN表示sizeof(struct stu)主要的目的是为了
在以下程序内减少书写并使阅读更加方便。结构stu定义为外部类型,
程序中的各个函数均可使用该定义。
    creat函数用于建立一个有n个结点的链表,它是一个指针函数,
它返回的指针指向stu结构。在creat函数内定义了三个stu结构的指
针变量。head为头指针,pf 为指向两相邻结点的前一结点的指针变
量。pb为后一结点的指针变量。在for语句内,用malloc函数?靡?
长度与stu长度相等的空间作为一结点,首地址赋予pb。然后输入结
点数据。如果当前结点为第一结点(i==0),则把pb值 (该结点指针)
赋予head和pf。如非第一结点,则把pb值赋予pf 所指结点的指针域
成员next。而pb所指结点为当前的最后结点,其指针域赋NULL。 再
把pb值赋予pf以作下一次循环准备。
    creat函数的形参n,表示所建链表的结点数,作为for语句的循
环次数。
图7.4表示了creat函数的执行过程。[例7.11]写一个函数,在链表中按学号查找该结点。TYPE * search (TYPE *head,int n)
{
    TYPE *p;
    int i;
    p=head;
    while (p->num!=n && p->next!=NULL)
    p=p->next;        /* 不是要找的结点后移一步*/
    if (p->num==n) return (p);
    if (p->num!=n&& p->next==NULL)
    printf ("Node %d has not been found!\n",n
}
    本函数中使用的符号常量TYPE与例7.10的宏定义相同,等于struct
 stu。函数有两个形参,head是指向链表的指针变量,n为要查找的
学号。进入while语句,逐个检查结点的num成员是否等于n,如果不
等于n且指针域不等于NULL(不是最后结点)则后移一个结点,继续循
环。如找到该结点则返回结点指针。 如循环结束仍未找到该结点则
输出“未找到”的提示信息。[例7.12]写一个函数,删除链表中的指定结点。
删除一个结点有两种情况:
1. 被删除结点是第一个结点。这种情况只需使head指向第二个结点
   即可。即head=pb->next。其过程如图7.5所示。2. 被删结点不是第一个结点,这种情况使被删结点的前一结点指向
   被删结点的后一结点即可。即pf->next=pb->next。其过程如图7.
   6所示。
函数编程如下:TYPE * delete(TYPE * head,int num)
{
    TYPE *pf,*pb;
    if(head==NULL)  /*如为空表, 输出提示信息*/
      { printf("\nempty list!\n");
        goto end;}
    pb=head;
 while (pb->num!=num && pb->next!=NULL)
 /*当不是要删除的结点,而且也不是最后一个结点时,继续循环*/
{pf=pb;pb=pb->next;}/*pf指向当前结点,pb指向下一结点*/
if(pb->num==num)
    {if(pb==head) head=pb->next;
     /*如找到被删结点,且为第一结点,则使head指向第二个结点,
                      否则使pf所指结点的指针指向下一结点*/
    else pf->next=pb->next;
    free(pb);
    printf("The node is deleted\n");}
  else
    printf("The node not been foud!\n");
end:
    return head;
}    函数有两个形参,head为指向链表第一结点的指针变量,num删
结点的学号。 首先判断链表是否为空,为空则不可能有被删结点。
若不为空,则使pb指针指向链表的第一个结点。
    进入while语句后逐个查找被删结点。找到被删结点之后再看是
否为第一结点,若是则使head指向第二结点(即把第一结点从链中删
去),否则使被删结点的前一结点(pf所指)指向被删结点的后一结点
(被删结点的指针域所指)。如若循环结束未找到要删的结点, 则输
出“末找到”的提示信息。最后返回head值。
[例7.13]写一个函数,在链表中指定位置插入一个结点。
    在一个链表的指定位置插入结点, 要求链表本身必须是已按某

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -