结构体与共用体

目录

  1. 概述
  2. 定义结构体类型变量的方法
  3. 结构体变量的引用
  4. 结构体变量的初始化
  5. 结构体数组
  6. 指向结构体类型数据的指针
  7. 用指针处理链表
  8. 共用体
  9. 枚举类型
  10. 用typedef定义类型

 

1.概述
问题定义:有时需要将不同类型的数据组合成一个有机的整体,以便于引用。

如:一个学生有学号/姓名/性别/年龄/地址等属性   int num; char name[20]; char sex; int age;   int char addr[30];

定义一个结构的一般形式为:
 struct 结构名
 {

成员表列

};

成员表列由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为:类型说明符 成员名;

struct student
  {
      int num;
      char name[20];
      char sex;
      int age;
      float score;
      char addr[30];
  }

 

2.定义结构体类型变量的方法

可以采取以下3种方法定义结构体类型变量:

 

(1)先声明结构体类型再定义变量名
例如:struct  student  student1, student2;
               |            |              |               |       
           类型名   结构体     变量名      变量名

定义了student1和student2为struct student类型的变量,即它们具有struct student类型的结构.

struct student
  {
      int num;
      char name[20];
      char sex;
      int age;
      float score;
      char addr[30];
  } student1, student2

 

在定义了结构体变量后,系统会为之分配内存单元。例如:  student1和student2在内存中各占 ? 个字节。( 4 + 20 + 1 + 4 + 4 + 30 =  67 )。不会占用67个字节,涉及到编译优化问题。

(2)在声明类型的同时定义变量
   这种形式的定义的一般形式为:
     struct 结构体名
     {
         成员表列
     }变量名表列;

 例如:

struct student
{   int num;
     char name[20];
     char sex;
     int age;
     float score;
     char addr[30];
}student1,student2;

 

(3) 直接定义结构体类型变量
   其一般形式为:
      struct
    {
      成员表列
  }变量名表列;

即不出现结构体名。

看图下定义:

结论:这是一个嵌套的定义

首先定义一个结构date,由month(月)、day(日)、year(年) 三个成员组成。

在定义并说明变量 boy1 和 boy2 时,其中的成员birthday被说明为date结构类型。成员名可与程序中其它变量同名,互不干扰。

struct date
{
    int month;
    int day;
    int year;
};

struct
{
    int num;
    char name[20];
    char sex;
    struct date birthday;
    float score;
}boy1, boy2;

 

3.结构体变量的引用
在定义了结构体变量以后,当然可以引用这个变量。但应遵守以下规则:   

(1) 不能将一个结构体变量作为一个整体进行输入和输出。
  例如: 打印student1的各个变量的值。不可以这样:printf(″%d,%s,%c,%d,%f,%\n″,student1);

正确引用结构体变量中成员的方式为:结构体变量名.成员名

student1.num表示student1变量中的num成员,即student1的num(学号)项。可以对变量的成员赋值,例如:student1.num=100;
“.”是成员(分量)运算符,它在所有的运算符中优先级最高,因此可以把student1.num作为一个整体来看待。上面赋值语句的作用是将整数100赋给student1变量中的成员num。
例子:

#include <stdio.h>

void main()
{
      struct student
      {
            int num;
            char *name;
            char sex;
            float score;
      } boy1, boy2;
      
      boy1.num = 007;
      boy1.name = "Jane";
      
      printf("Please input sex and score\n");
      scanf("%c %f", &boy1.sex, &boy1.score);
      
      boy2 = boy1;

      printf("Number = %d\nName = %s\n", boy2.num, boy2.name);
      printf("Sex = %c\nScore = %f\n", boy2.sex, boy2.score);
      
}

 

(2) 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。

对上面定义的结构体变量student1, 可以这样访问各成员:
    student1.num
    student1.birthday.month

/***********************************************************************************************/
/* 注意:                                                                                      */
/* 不能用student1.birthday来访问student1变量中的成员birthday,因为birthday本身是一个结构体变量。*/
/***********************************************************************************************/

#include <stdio.h>

void main()
{
      struct date            
      {          
            int month;            
            int day;            
            int year;            
      };
      
      struct
      {            
            int num;            
            char name[20];            
            char sex;            
            struct date birthday;            
            float score;            
      } boy1, boy2;
      
      
      printf("Please input birthday(YY:) ");
      scanf("%d", &boy1.birthday.year);
      printf("Please input birthday(MM:) ");
      scanf("%d", &boy1.birthday.month);
      printf("Please input birthday(DD:) ");
      scanf("%d", &boy1.birthday.day);
      printf("\n");

      boy2 = boy1;

      printf("boy1's birthday is %d-%d-%d\n", boy1.birthday.year, boy1.birthday.month, boy1.birthday.day);
      printf("boy2's birthday is %d-%d-%d\n", boy2.birthday.year, boy2.birthday.month, boy2.birthday.day);
}

 

(3) 对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。

例如:
   student2.score = student1.score;
   sum = student1.score + student2.score;
   student1.age++;  
   ++student2.age;

(4) 可以引用结构体变量成员的地址,也可以引用结构体变量的地址。

例子:

#include <stdio.h>

void main()
{
      struct student
      {
            int num;
            char *name;
            char sex;
            float score;
      } boy1;
      
      boy1.num = 007;
      boy1.name = "Jane";
      
      printf("The address of struct is %o :\n", &boy1 );
      printf("The address of num is %o :\n", &boy1.num );

}

结果:地址相同,结构体的地址同首个结构体变量地址,类似数组。


但不能用以下语句整体读入结构体变量:
  scanf(″%d,%s,%c,%d,%f,%s″,&student1);

结构体变量的地址主要用作函数参数,传递结构体变量的地址。

4.结构体变量的初始化

我们可以这么对结构体进行初始化!例子:

#include <stdio.h>

void main()
{
      struct student    /*定义结构*/
      {            
            int num;            
            char *name;            
            char sex;            
            float score;            
      }boy1, boy2 = { 102, "Jane", 'M', 98.5 };

      boy1 = boy2;
      
      printf("Number = %d\nName = %s\nScore = %d\n", boy1.num, boy1.name, boy1.score);
      printf("\n\n");
      printf("Number = %d\nName = %s\nScore = %d\n", boy2.num, boy2.name, boy2.score);
      
      
}
View Code

 

5.结构体数组

一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。

如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。

结构体数组与以前介绍过的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。

实例学习:通讯录.exe

#include"stdio.h"

#define NUM 3

struct person
{      
      char name[20];      
      char phone[10];      
};

void main()
{      
      struct person man[NUM];  //结构体数组    
      int i;
      
      for( i=0; i < NUM; i++)            
      {            
            printf("input name:\n");            
            gets(man[i].name);            
            printf("input phone:\n");            
            gets(man[i].phone);            
      }
      
      printf("name\t\t\tphone\n\n");
      
      for( i=0; i < NUM; i++)
      {
            printf("%s\t\t\t%s\n",man[i].name,man[i].phone);
      }
}
View Code

 

定义结构体数组和定义结构体变量的方法相仿,只需说明其为数组即可。例如:

struct student
{
        int num;
        char name[20];
        char sex;
        int age;
        float score;
        char addr[30];
 };
struct student student[3];

也可以这样:

struct student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
 }student[3];

结构体数组的初始化与其他类型的数组一样,对结构体数组可以初始化。例如:

struct student
{
    int num;
    char name[20]; 
    char sex;     
    int age; 
    float score; 
    char addr[30];
}stu[2]= {
             {101,″LiLin″,′M′,1887.5,″Beijing″},        
                {102,″Zhang″,′F′,1999,″Shanghai″}
         };            

当然,数组的初始化也可以用以下形式:

struct student
{
    int num;
    …
};

struct student str[]{{…},{…},{…}};

即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。

结构体数组应用实例:对候选人得票的统计程序。设有3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define NUM 4
struct person
{
      char name[20];
      int count;
}candidate[NUM] = {
                  {"小甲鱼", 0},
                  {"苍井空", 0}, 
                  {"松岛枫", 0},
                  {"莫丁丁", 0}
                };

char *winner();

void main()
{
      int i, j;
      char candidate_name[20];

      printf("欢迎进入良好公民评选投票系统:() \n\n");
      printf("候选人有: 小甲鱼, 苍井空, 松岛枫, 莫丁丁(路人甲)\n\n");
      
      for( i=1; i <= 10; i++ )
      {
            printf("第 %2d 位投票, 请写下支持的候选人名字: ", i);
            scanf("%s", candidate_name);
            for( j=0; j < NUM; j++ )
            {
                  if( 0 == strcmp(candidate_name, candidate[j].name) )
                  {
                        candidate[j].count++;
                  }
            }
      }
      printf("\n");

      for( i=0; i < 4; i++ )
      {
            printf("%s 同学得票数为: %d\n", candidate[i].name, candidate[i].count );
      }
      printf("\n");
      printf("本次投票活动的胜利者的: %s", winner() );

      printf("\n");
      system("pause");
}

char *winner()
{
      int i =0 , winner = i;
      
      for( i=1; i < NUM; i++ )
      {
            if( candidate[winner].count < candidate[i].count )
            {
                  winner = i;
            }
      }
      
      return candidate[winner].name;
}
View Code

 

6.指向结构体类型数据的指针

一个结构体变量的指针就是该结构体变量所占据的内存段的起始地址。

可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。(指针伟大吧,指啥都行~)

指针变量也可以用来指向结构体数组中的元素。结构指针变量说明的一般形式为:struct 结构名 *结构指针变量名

例如,在前面的例题中定义了stu这个结构,如要说明一个指向stu的指针变量pstu,可写为:struct stu *pstu;

当然也可在定义stu结构时同时说明pstu。与前面讨论的各类指针变量相同,结构指针变量也必须要先赋值后才能使用。

赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。
如果boy是被说明为stu类型的结构变量,则:
    pstu=&boy是正确的。
    pstu=&stu是错误的。
但是为什么呢?因为,结构名和结构变量是两个不同的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对它分配内存空间。只有当某变量被说明为这种类型的结构时,才对该变量分配存储空间。因此上面&stu这种写法是错误的,不可能去取一个结构名的首地址。有了结构指针变量,就能更方便地访问结构变量的各个成员。

其访问的一般形式为:
    (*结构指针变量).成员名
或为:
        结构指针变量->成员名

例如:(*pstu).num

或者:pstu->num

下面通过一个简单例子来说明指向结构体变量的指针变量的应用。

#include <stdio.h>

struct stu
{      
      int num;     
      char *name;      
      char sex;      
      float score;      
} boy1 = {102, "Fishc", 'M', 78.5};

void main()
{
      struct stu *pstu;
      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);      
}
View Code

 

结构指针变量作函数参数

  将一个结构体变量的值传递给另一个函数,有3个方法:

  1. 用结构体变量的成员作参数
  2. 用结构体变量作实参
  3. 用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参

 例题:有一个结构体变量stu,内含学生学号、姓名和3门课程的成绩。通过调用函数print中将它们输出。

  • 先用结构体变量作函数参数:
    #include <stdio.h>
    #include <string.h>
    
    struct student
    {
          int num;
          char *name;//char name[20],对应下边的strcpy(stu.name, "Fishc.com");
          float score[3];
    };
    
    void print(struct student);
    
    void main()
    {
          struct student stu;
          
          stu.num = 8;
          stu.name = "Fishc.com!";//strcpy(stu.name, "Fishc.com");
          stu.score[0] = 98.5;
          stu.score[1] = 99.0;
          stu.score[2] = 99.5;
    
          print( stu );
    }
    
    void print( struct student stu )
    {
          printf("\tnum     : %d\n", stu.num);
          printf("\tname    : %s\n", stu.name);
          printf("\tscore_1 : %5.2f\n", stu.score[0]);
          printf("\tscore_2 : %5.2f\n", stu.score[1]);
          printf("\tscore_3 : %5.2f\n", stu.score[2]);
          printf("\n");
    }
    View Code
  • 改用指向结构体变量的指针作实参:
    #include <stdio.h>
    #include <string.h>
    
    struct student
    {
          int num;
          char name[20];
          float score[3];
    };
    
    void print(struct student *);
    
    void main()
    {
          struct student stu;
          
          stu.num = 8;
          strcpy(stu.name, "Fishc.com!"); 
          stu.score[0] = 98.5;
          stu.score[1] = 99.0;
          stu.score[2] = 99.5;
    
          print( &stu );
    }
    
    void print( struct student *p )
    {
          printf("\tnum     : %d\n", p -> num);
          printf("\tname    : %s\n", p -> name);
          printf("\tscore_1 : %5.2f\n", p -> score[0]);
          printf("\tscore_2 : %5.2f\n", p -> score[1]);
          printf("\tscore_3 : %5.2f\n", p -> score[2]);
          printf("\n");
    }
    View Code

     

 

7.用指针处理链表

 动态存储分配:

  在数组一章中,曾介绍过数组的长度是预先定义好的,在整个程序中固定不变。C语言中不允许动态数组类型。
  例如:int a[n];   用变量表示长度,想对数组的大小作动态说明,这是错误的。
  但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。

  所以对于这种问题,用数组的办法很难解决!
  为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。
  常用的内存管理函数有以下三个:
    1. 分配内存空间函数 malloc、calloc
    2. 释放内存空间函数 free
  

  malloc函数:函数原型为void *malloc(unsigned int size);  
    其作用是在内存的动态存储区中分配一个长度为size的连续空间(size是一个无符号数)。
    此函数的返回值是一个指向分配域起始地址的指针(类型为void)。
    如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)。
  calloc函数:函数原型为void *calloc(unsigned n, unsigned size);
    其作用是在内存的动态存储区中分配n个长度为size的连续空间。
    函数返回一个指向分配域起始地址的指针;
    如果分配不成功,返回NULL。
    用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。

  free函数:函数原型为void free(void *p);
    其作用是释放由p指向的内存区,使这部分内存区能被其他变量使用。
    p是最近一次调用calloc或malloc函数时返回的值。
    free函数无返回值。

 链表:

  什么是链表?链表是一种常见的重要的数据结构,是动态地进行存储分配的一种结构。
  链表的组成:
    头指针:存放一个地址,该地址指向第一个元素
    
结点:用户需要的实际数据和链接节点的指针

  我们尝试根据下图建立链表:

   

#include <stdio.h>

struct student
{
      long num;  
      float score;
      struct student *next;
};

void main()
{
      struct student a, b, c, *head;

      a.num = 10101; 
      a.score = 89.5;
      b.num = 10103;
      b.score = 90;
      c.num = 10107;
      c.score = 85;

      head = &a;
      a.next = &b;
      b.next = &c;
      c.next = NULL;

      do
      {
            printf("%ld %5.1f\n", head->num, head->score);
            head = head->next;
      }while( head != NULL );
}
View Code

 

  建立动态链表:所谓建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系。

  作业:根据下面的分析写一程序建立一个含有学生(学号,成绩)数据的单向动态链表。(约定:我们约定学号不会为零,如果输入的学号为0,则表示建立链表的过程完成,该结点不应连接到链表中。)
  其流程图为:

    

  分析:1-4为链表的创建,5为链表的输出

    1.我们约定学号不会为零,如果输入的学号为0,则表示建立链表的过程完成,该结点不应连接到链表中。如果输入的p1->num不等于0,则输入的是第一个结点数据(n=1),令head=p1,即把p1的值赋给head,也就是使head也指向新开辟的结点p1所指向的新开辟的结点就成为链表中第一个结点。  

      

    2.再开辟另一个结点并使p1指向它,接着输入该结点的数据。如果输入的p1->num≠0,则应链入第2个结点(n=2), 将新结点的地址赋给第一个结点的next成员。接着使p2=p1,也就是使p2指向刚才建立的结点。

      

    3.再开辟一个结点并使p1指向它,并输入该结点的数据。在第三次循环中,由于n=3(n≠1),又将p1的值赋给p2->next,也就是将第3个结点连接到第2个结点之后,并使p2=p1,使p2指向最后一个结点。

       

    4.再开辟一个新结点,并使p1指向它,输入该结点的数据。由于p1->num的值为0,不再执行循环,此新结点不应被连接到链表中。将NULL赋给p2->next。建立链表过程至此结束,p1最后所指的结点未链入链表中,第三个结点的next成员的值为NULL,它不指向任何结点

      

     5.实现链表输出:首先要知道链表第一个结点的地址,也就是要知道head的值。然后设一个指针变量p,先指向第一个结点,输出p所指的结点,然后使p后移一个结点,再输出,直到链表的尾结点。如此我们得到这样的流程图:

                 

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>

#define LEN sizeof(struct student)  // student结构的大小

struct student *creat();            //创建链表
void print(struct student *head);   //打印链表

struct student
{
      int num;
      float score;
      struct student *next;
};

int n; //全局变量,用来记录存放了多少数据。

void main()
{
      struct student *stu;

      stu = creat();
      print( stu );

      printf("\n\n");
      system("pause");
}
//创建链表
struct student *creat()
{
      struct student *head;
      struct student *p1, *p2;
  
      p1 = p2 = (struct student *)malloc(LEN);  // LEN是student结构的大小

      printf("Please enter the num :");
      scanf("%d", &p1->num);
      printf("Please enter the score :");
      scanf("%f", &p1->score);

      head = NULL;     
      n = 0;    
      
      while( 0 != p1->num )
      {
            n++;
            if( 1 == n )
            {
                  head = p1;                 
            }
            else
            {
                  p2->next = p1;
            }

            p2 = p1;

            p1 = (struct student *)malloc(LEN);

            printf("\nPlease enter the num :");
            scanf("%d", &p1->num);
            printf("Please enter the score :");
            scanf("%f", &p1->score);
      }

      p2->next = NULL;

      return head;
}
//实现链表输出
void print(struct student *head)
{
      struct student *p;
      printf("\nThere are %d records!\n\n", n);

      p = head;
      if( NULL != head )
      {
            do
            {
                  printf("学号为 %d 的成绩是: %f\n", p->num, p->score);
                  p = p->next;
            }while( NULL != p );
      }
}
View Code

 

  对链表的删除操作:

    从一个动态链表中删去一个结点,并不是真正从内存中把它抹掉,而是把它从链表中分离开来,只要撤销原来的链接关系即可。

      

    题目:写一函数以删除动态链表中指定的结点。
    解题思路:
      一、从p指向的第一个结点开始,检查该结点中的num值是否等于输入的要求删除的那个学号。
      二、如果相等就将该结点删除,如不相等,就将p后移一个结点,再如此进行下去,直到遇到表尾为止

      三、可以设两个指针变量p1和p2,先使p1指向第一个结点 。
      四、如果要删除的不是第一个结点,则使p1后移指向下一个结点(将p1->next赋给p1),在此之前应将p1的值赋给p2 ,使p2指向刚才检查过的那个结点。
      五、将以上几点我们综合得出算法流程图:
            

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>

#define LEN sizeof(struct student)  // student结构的大小

struct student *creat();   // 创建链表
struct student *del( struct student *head, int num);  // del函数用于删除结点, *head即链表
                                                      // 的头指针, num是要删除的结点num。
void print(struct student *head);   // 打印链表

struct student
{
      int num;
      float score;
      struct student *next;
};

int n;    // 全局变量,用来记录存放了多少数据。

void main()
{
      struct student *stu, *p;
      int n;

      stu = creat();
      p = stu;
      print( p );

      printf("Please enter the num to delete: ");
      scanf("%d", &n);
      print( del(p, n) );
      
      printf("\n\n");
      system("pause");
}

struct student *creat()
{
      struct student *head;
      struct student *p1, *p2;
  
      p1 = p2 = (struct student *)malloc(LEN);  // LEN是student结构的大小

      printf("Please enter the num :");
      scanf("%d", &p1->num);
      printf("Please enter the score :");
      scanf("%f", &p1->score);

      head = NULL;     
      n = 0;    
      
      while( p1->num )
      {
            n++;
            if( 1 == n )
            {
                  head = p1;                 
            }
            else
            {
                  p2->next = p1;
            }

            p2 = p1;
            p1 = (struct student *)malloc(LEN);

            printf("\nPlease enter the num :");
            scanf("%d", &p1->num);
            printf("Please enter the score :");
            scanf("%f", &p1->score);
      }

      p2->next = NULL;

      return head;
}

void print(struct student *head)
{
      struct student *p;
      printf("\nThere are %d records!\n\n", n);

      p = head;
      if( head )
      {
            do
            {
                  printf("学号为 %d 的成绩是: %f\n", p->num, p->score);
                  p = p->next;
            }while( p );
      }
}

struct student *del( struct student *head, int num)
{
      struct student *p1, *p2;
      
      if( NULL == head ) // 如果头结点指向NULL,这是一个空链表。纯属忽悠T_T
      {
            printf("\nThis list is null!\n");
            goto END;
      }

      p1 = head;
      while( p1->num != num && p1->next != NULL)
      {
            p2 = p1;
            p1 = p1->next;
      }
      if( num == p1->num )
      {
            if( p1 == head )      // 当将要删除的结点位于头结点的时候
            {
                  head = p1->next;
            }
            else                  // 一般情况
            {
                  p2->next = p1->next; 
            }

            printf("\nDelete No: %d succeed!\n", num);
            n = n-1; // n是作为一个全局变量,用来记录链表的数据数。
      }
      else
      {
            printf("%d not been found!\n", num);
      }

END:
      return head;
}
View Code

 

  对链表的插入操作:

    对链表的插入是指将一个结点插入到一个已有的链表中。 为了能做到正确插入,必须解决两个问题: ① 怎样找到插入的位置; ② 怎样实现插入。

    我们可以先用指针变量p0指向待插入的结点,p1指向第一个结点。将p0->num与p1->num相比较,如果p0->num>p1-> num ,此时将p1后移,并使p2指向刚才p1所指的结点。

      

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>

#define LEN sizeof(struct student)  // student结构的大小

struct student *creat();   //创建链表
struct student *del(struct student *head, int num);  //del函数用于删除结点, *head即链表
                                                     //的头指针, num是要删除的结点num。
struct student *insert(struct student *head, struct student *stu_2);  // 第一个参数:需要被插入的链表
                                                                      // 第二个参数:待插入的结构的地址

void print(struct student *head);   //打印链表

struct student
{
      int num;
      float score;
      struct student *next;
};

int n; //全局变量,用来记录存放了多少数据。

void main()
{
      struct student *stu, *p, stu_2;
      int n;
      
      stu = creat();
      p = stu;
      print( p );
      
      printf("\nPlease input the num to delete: ");
      scanf("%d", &n);
      print( del(p, n) );

      printf("\nPlease input the num to insert: ");
      scanf("%d", &stu_2.num);
      printf("Please input the score: ");
      scanf("%f", &stu_2.score);

      p = insert(stu, &stu_2);
      print( p );


      

      printf("\n\n");
      system("pause");
}

struct student *creat()
{
      struct student *head;
      struct student *p1, *p2;
      
      p1 = p2 = (struct student *)malloc(LEN);  // LEN是student结构的大小
      
      printf("Please enter the num :");
      scanf("%d", &p1->num);
      printf("Please enter the score :");
      scanf("%f", &p1->score);
      
      head = NULL;     
      n = 0;    
      
      while( p1->num )
      {
            n++;
            if( 1 == n )
            {
                  head = p1;                 
            }
            else
            {
                  p2->next = p1;
            }
            
            p2 = p1;
            p1 = (struct student *)malloc(LEN);
            
            printf("\nPlease enter the num :");
            scanf("%d", &p1->num);
            printf("Please enter the score :");
            scanf("%f", &p1->score);
      }
      
      p2->next = NULL;
      
      return head;
}

void print(struct student *head)
{
      struct student *p;
      printf("\nThere are %d records!\n\n", n);
      
      p = head;
      if( head )
      {
            do
            {
                  printf("学号为 %d 的成绩是: %f\n", p->num, p->score);
                  p = p->next;
            }while( p );
      }
}

struct student *del( struct student *head, int num)
{
      struct student *p1, *p2;
      
      if( NULL == head )
      {
            printf("\nThis list is null!\n");
            goto end;
      }
      
      p1 = head;
      while( p1->num != num && p1->next != NULL)
      {
            p2 = p1;
            p1 = p1->next;
      }
      if( num == p1->num )
      {
            if( p1 == head )
            {
                  head = p1->next;
            }
            else
            {
                  p2->next = p1->next;
            }
            
            printf("Delete No: %d succeed!\n", num);
            n = n-1;
      }
      else
      {
            printf("%d not been found!\n", num);
      }
      
end:
      return head;
}

struct student *insert(struct student *head, struct student *stu_2)
{
      struct student *p0, *p1, *p2;
      
      p1 = head;
      p0 = stu_2;
      if( NULL == head ) 
      {
            head = p0;
            p0->next = NULL;
      }
      else
      {
            while( (p0->num > p1->num) && (p1->next != NULL) ) //两种情况推出while,一:
            {
                  p2 = p1;
                  p1 = p1->next;
            }
            
            if( p0->num <= p1->num )
            {
                  if( head == p1 )   // p1是头结点,插入头部
                  {
                        head = p0;  
                  }
                  else               // 普通情况,插入中间
                  {
                        p2->next = p0;
                  }

                  p0->next = p1;
            }
            else   // p0的num最大,插入到末尾
            {
                  p1->next = p0;
                  p0->next = NULL;
            }
      }

      n = n+1;   // 由于插入了,所以增加了一位数据成员进入链表中。
      
      return head;
}
View Code

 

      

8.共用体

 

  共用体的概念 : 使几个不同的变量共占同一段内存的结构称为 “共用体”类型的结构。

  定义共用体类型变量的一般形式为:

    union 共用体名

      {

        成员表列

      }变量表列;

//例如:

 union data             
{                  
    int i;               
    char ch;         
    float f;             
}a,b,c;                
                     
//或者
 union data             
{                  
    int i;               
    char ch;         
    float f;             
};
 union data a,b,c;

  

  共用体和结构体的比较:

  • 结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。

  • 共用体变量所占的内存长度等于最长的成员的长度。 例如: 上面定义的“共用体”变量a、b、c各占4个字节(因为一个实/整型变量占4个字节),而不是各占4+1+4=9个字节。

      

  共用体变量的引用方式:只有先定义了共用体变量才能引用它,而且不能引用共用体变量,而只能引用共用体变量中的成员。例如:前面定义了a、b、c为共用体变量:

  • a.i (引用共用体变量中的整型变量i)
  • a.ch(引用共用体变量中的字符变量ch)
  • a.f (引用共用体变量中的实型变量f)

 

  共用体类型数据的特点:

  1. 同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种

  2. 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。

  3. 共用体变量的地址和它的各成员的地址都是同一地址。

  4. 不能对共用体变量名赋值,也不能企图引用变量名来得到一个值,又不能在定义共用体变量时对它初始化。

  5. 不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针 。

  6. 共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。

 

  应用情况:设有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。可以看出,学生和教师所包含的数据是不同的。现要求把它们放在同一表格中。

      

    处理算法:

      

#include <stdio.h>

struct
{
      int num;
      char name[10];
      char sex;
      char job;
      union
      {
            int banji;
            char position[10];
      }category;
}person[2];  //为了方便先假设一个学生一个老师。

void main()
{
      int i;
      for(i=0; i < 2; i++)
      {
            printf("Please input the num: ");
            scanf("%d", &person[i].num);
            printf("Please input the name: ");
            scanf("%s", person[i].name);
            fflush(stdin);
            printf("Please input the sex(M/F): ");
            scanf("%c", &person[i].sex);
            fflush(stdin);
            printf("Please input the job(s/t): ");
            scanf("%c", &person[i].job);
            fflush(stdin);

            if( person[i].job == 's' )
            {
                  printf("Please input the class: ");
                  scanf("%d", &person[i].category.banji);
                  fflush(stdin);
            }
            else if( person[i].job == 't' )
            {
                  printf("Please input the position: ");
                  scanf("%s", person[i].category.position);
                  fflush(stdin);
            }
            else
            {
                  printf("Input Error!!\n");
            }            
            
            printf("\n");
      }
      
      // 以下是打印数据……

      printf("No.    name    sex job class/position\n");
      for( i=0; i < 2; i++ )
      {
            if( person[i].job == 's')
            {
                  printf("%-6d%-10s%-3c%-3c%10d\n", person[i].num, 
                        person[i].name, person[i].sex, person[i].job, 
                        person[i].category.banji);
            }
            else
            {
                  printf("%-6d%-10s%-3c%-3c%10s\n", person[i].num, 
                        person[i].name, person[i].sex, person[i].job, 
                        person[i].category.position);
            }
      }
}
View Code

 

 

9.枚举类型

  在实际问题中,有些变量的取值被限定在一个有限的范围内。 例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等。 如果把这些量说明为整型,字符型或其它类型显然是不妥当的。 为此,C语言提供了一种称为“枚举”的类型。设有变量a,b,c被说明为上述的weekday:

//可采用下述任一种方式:

enum weekday{ sun,mon,tue,wed,thu,fri,sat };
enum weekday a, b, c;

//或者为:
enum weekday{ sun,mon,tue,wed,thu,fri,sat }a, b, c;

//或者为:
enum { sun,mon,tue,wed,thu,fri,sat }a, b, c;

  

  枚举类型中需要注意的地方:

  • 在“枚举”类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围。

  • 应该说明的是,枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。

  • 在枚举值表中应罗列出所有可用值。这些值也称为枚举元素。

  • 在C编译中,对枚举元素按常量处理,故称枚举常量。它们不是变量,不能对它们赋值。

  • 枚举元素作为常量,它们是有值的,C语言编译按定义时的顺序使它们的值为0,1,2…

  • 枚举值可以用来作判断比较。

  • 一个整数不能直接赋给一个枚举变量。

  

  例子一:enum.c

#include <stdio.h>

void main()
{
      
      enum weekday{sun, mon, tue, wed, thu, fri, sat}a, b, c;
      
      a = sun; // a = 1;这样是不行的……
      b = mon;
      c = tue;
      
      printf("结果:%d, %d, %d", a, b, c); //结果:0,1,2
      printf("\n\n");
}
View Code

  例子二:enum2.c

#include <stdio.h>

void main()
{
      enum boy{Tom, Danny, Gan, LiLei}month[31], j;
      int i;
      
      j = Tom;
      
      for( i=1; i <= 30; i++ )
      {
            month[i] = j;
            j++;
            
            if( j > LiLei ) 
            {
                  j = Tom;
            }
      }
      
      for( i=1; i <= 30; i++)
      {
            switch( month[i] )
            {
            case Tom: printf(" %4d  %s\t", i, "Tom"); 
                  break;
            case Danny: printf(" %4d  %s\t", i, "Danny"); 
                  break;
            case Gan: printf(" %4d  %s\t", i, "Gan"); 
                  break;
            case LiLei: printf(" %4d  %s\t", i, "LiLei"); 
                  break;
            default:
                  break;
            }
      }
      
      printf("\n");
}
View Code

 

 

10.用typedef定义类型

  用typedef声明新的类型名来代替已有的类型名:

  • 声明INTEGER为整型:typedef  int  INTEGER
    #include <stdio.h>
    
    typedef int INTEGER;
    
    void main()
    {
          INTEGER i = 1;
          int j = 2;
    
          printf("%d, %d\n\n", i, j);
    }
    View Code

     

  • 声明结构类型:
    Typedef  struct{
     int month;
     int day;
     int year;}DATE;

      举例:

    #include <stdio.h>
    
    typedef struct
    {
          int month;
          int day;
          int year;
    }DATE;
    
    void main()
    {
          DATE date_one;
    
          date_one.month = 12;
          date_one.day = 31;
          date_one.year = 2012;
    
          printf("%d - %d - %d \n", date_one.year, date_one.month, date_one.day);
    }
    View Code

     

  • 声明NUM为整型数组类型 :typedef  int  NUM[100]; 
    #include <stdio.h>
    
    typedef int NUM[100];
    
    void main()
    {
          NUM num = {0};
          printf("%d\n\n", sizeof(num));
    }
    View Code

     

  • 声明STRING为字符指针类型 :typedef char* STRING;

    #include <stdio.h>
    
    typedef char* P;
    
    void main()
    {
          P p1;
    
          p1 = "I love Fishc.com";
          printf("%s\n", p1);
    }
    View Code

     

  • 声明 POINTER 为指向函数的指针类型,该函数返回整型值: typedef int (*POINTER)();

    #include <stdio.h>
    
    typedef void (*P)();
    
    void fun();
    void main()
    {
          P p1; // void (*p1)();
          char a[10] = "Fishc.com!";
    
          printf("%d %d\n", a, &a);//数组地址和数组名地址相同
    
          p1 = fun;//等同于p1 = &fun; 函数名称和函数地址相同,与数组地址和数组名地址道理相同
          (p1)();
    }
    
    void fun()
    {
          printf("I love Fishc.com!\n");
    }
    View Code

     

   用typedef定义类型的方法:先按定义变量的方法写出定义体(如:int i) ,将变量名换成新类型名(例如:将i换成COUNT)。即在最前面加typedef(例如:typedef int COUNT),然后可以用新类型名去定义变量 (例如:COUNT i, j;)

 

  关于 typedef的一些说明:

  • 用typedef 可以声明各种类型名,但不能用来定义变量。

  • 用typedef 只是对已经存在的类型增加一个类型名,而没有创造新的类型。

  • 当不同源文件中用到同一类型数据时,常用typedef 声明一些数据类型,把它们单独放在一个文件中,然后在需要用到它们的文件中用#include命令把它们包含进来。

  • 使用typedef 有利于程序的通用与移植。

  • typedef与#define有相似之处,例如:typedef int COUNT;#define COUNT int 的作用都是用COUNT 代表 int。但是,它们二者是不同的。

  • #define是在预编译时处理的,它只能作简单的字符串替换,而typedef是在编译时处理的。实际上它并不是作简单的字符串替换,而是采用如同定义变量的方法那样来声明一个类型。

 

  区别: typedef 和 define

    typedef  (int*)  p1;和 #define p2 int*

   另外注意,一个有分号,一个没有分号!

 

 







 

posted @ 2018-12-06 11:36  耐烦不急  阅读(2277)  评论(0编辑  收藏  举报