C程序设计(谭浩强)随书代码验证-第九章 用户自己建立数据类型


C语言提供了一些由系统已定义好的数据类型,如:int,float,char等,用户可以在程序中用它们定义变量,解决一般的问题,但是人们要处理的问题往往比较复杂,只有系统提供的类型还不能满足应用的要求,C语言允许用户根据需要自己建立一些数据类型,用它来定义变量。

源码

源码: github.

9.1定义和使用结构体变量

问题背景:
将有内在联系的变量组成一个组合数据

设想:数组:一个数组只能存放同一类型的数据

解决办法:C语言允许用户自己建立由不同数据类型组成的组合型的数据结构,称之为结构体
举例:

struct Student
{
	int num;
	char name[20];
	char sex;
	int age;
	float score;
	char address[30];
};					//注意:此处有一个分号

形式:
struct 结构体名
{
成员列表;(类型名 成员名;)
}

定义结构体类型变量

  1. 先声明结构体类型,在定义该类型的变量
    在这里插入图片描述
    -2. 在生声明类型的同时定义变量
    在这里插入图片描述
  2. 不指定类型名而直接定义结构体类型变量
    在这里插入图片描述
    在这里插入图片描述

结构体类型变量的 初始化 和 引用

9-1在定义时可初始化

在这里插入图片描述

#include <stdio.h>
int main()
{
	struct student
	{
		long int num;
		char name[20];
		char sex;
		char addr[20];
	}a={10101,"Li Lin",'M',"123 Beijing Road"};
	
	printf("NO.:%ld\nname:%s\nsex:%c\naddress:%s\n",a.num,a.name,a.sex,a.addr);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述

#include <stdio.h>
int main()
{
	struct student                      // 声明结构体类型struct student 
	{
		int num;
		char name[20];
		float score;
	}student1,student2;                 // 定义两个结构体变量seudent1,student2 
	
	scanf("%d%s%f",&student1.num,student1.name, &student1.score);    //输入学生1敌数据
	scanf("%d%s%f",&student2.num,student2.name, &student2.score);    //输入学生1敌数据
	printf("The higher score is:\n");
	if (student1.score>student2.score)
		printf("%d  %s  %6.2f\n",student1.num,student1.name, student1.score);
	else if (student1.score<student2.score)
		printf("%d  %s  %6.2f\n",student2.num,student2.name, student2.score);
	lse
	{
		printf("%d  %s  %6.2f\n",student1.num,student1.name, student1.score);
		printf("%d  %s  %6.2f\n",student2.num,student2.name, student2.score);
	}
	return 0;
}

在这里插入图片描述

9.2使用结构体数组

9-3定义结构体数组

在这里插入图片描述

#include <string.h>
#include <stdio.h>
struct person                              	// 声明结构体类型struct person
{
	char name[20];                          // 候选人姓名
	int count;                              // 候选人得票数 
}leader[3]={"Li",0,"Zhang",0,"Sun",0};    	// 定义结构体数组并初始化

int main()
{
	int i,j;
	char leader_name[20];                    // 定义字符数组 
	for (i=1;i<=10;i++)
	{
		scanf("%s",leader_name);              // 输入所选的候选人姓名  
		for(j=0;j<3;j++)
			if(strcmp(leader_name,leader[j].name)==0) 
				leader[j].count++;
	}
	printf("\nResoult:\n");
	for(i=0;i<3;i++)
		printf("%5s:%d\n",leader[i].name,leader[i].count);
	
	return 0;
}

在这里插入图片描述
在这里插入图片描述

9-4结构体数组的应用举例

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
struct student                                       // 声明结构体类型struct student  
{
	int num;
	char name[20];
	float score;
}; 
  
int main()
{
	struct student stu[5]={{10101,"Zhang",78},{10103,"Wang",98.5},{10106,"Li",86},
	      	{10108,"Ling",73.5},{10110,"Fun",100}};    // 定义结倒构体数组并初始化 
	struct student temp;                              //定义结构体变量temp,用作交换时的临时变量  
	const int n=5;
	int i,j,k;
	
	printf("The order is:\n");
	for(i=0;i<n-1;i++)
	{
		k=i;
		for(j=i+1;j<n;j++)
		 	if(stu[j].score>stu[k].score)                // 进行成绩的比较   
				k=j;
		
		temp=stu[k];
		stu[k]=stu[i];
		stu[i]=temp;           // stu[k]和stu[i]元素互换  
	}
	for(i=0;i<n;i++)
		printf("%6d %8s %6.2f\n",stu[i].num,stu[i].name,stu[i].score);
		
	printf("\n");
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9.3 结构体指针

指向结构体对象的指针变量即可以指向结构体变量,也可指向结构体数组中的元素
在这里插入图片描述

9-5 结构体指针变量定义 与 使用

在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main()
{
	struct student
	{
		long num;
		char name[20];
		char sex;
		float score;
	};
	
	struct student stu_1;          // 定义struct student类型的变量stu_1 
	struct student * p;            // 定义指向struct student 类型数据的指针变量p 
	
	p=&stu_1;                      // p指向stu_1 
	stu_1.num=10101;               // 对结构体变量的成员赋值 
	strcpy(stu_1.name,"Li Lin");
	stu_1.sex='M';
	stu_1.score=89.5;
	
	printf("No.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n", stu_1.num, stu_1.name, stu_1.sex, stu_1.score);           // 输出结果 
	printf("\nNo.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n", (*p).num, (*p).name, (*p).sex, (*p).score);
	printf("\nNo.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n", p->num, p->name, p->sex, p->score);
	return 0;
} 

在-这里插入图片描述
在这里插入图片描述

9-6指向结构体数组的指针

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
struct student
{
	int num;
	char name[20];
	char sex;
	int age;
};
struct student stu[3]=
	{{10101,"Li Lin",'M',18},{10102,"Zhang Fun",'M',19},{10104,"Wang Min",'F',20}};              // 定义结构体数组并初始化 

int main()
{
	struct student *p;                                            //定义指向struct student结构体的数组 
	printf(" No.  Name                 sex age\n");

	for (p=stu;p<stu+3;p++)
		printf("%5d %-20s %2c %4d\n",p->num, p->name, p->sex, p->age);
	
	return 0;
}            

在这里插入图片描述

9-7用结构体变量 和 结构体变量的指针作函数参数

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

  1. 用结构体变量的成员作参数
  2. 用结构体变量作实参
  3. 用指向结构体变量(或数组元素)的指针做实参
    在这里插入图片描述
    在这里插入图片描述
#include <stdio.h>
#define N 3                                     // 学生数为3  
struct student                                  // 建立结构体类型struct student  
{
	int num;                                      // 学号  
	char name[20];                                // 姓名   
	float score[3];                               // 3门课成绩  
	float aver;                                   // 平均成绩  
};

int main()
{
	void input(struct student stu[]);             // 函数声明  
	struct student max(struct student stu[]);     // 函数声明    
	void print(struct student stu);               // 函数声明  
	struct student stu[N],*p=stu;                 // 定义结构体数组和指针  
	
	input(p);                                     // 调用input函数  
	print(max(p));                                // 调用print函数,以max函数的返回值作为实参  
	return 0;
}

void input(struct student stu[])   // 定义input 函数  
{
	int i;
	printf("请输入各学生的信息:学号、姓名、三门课成绩:\n");
	for(i=0;i<N;i++)
	{
		scanf("%d %s %f %f %f",&stu[i].num,stu[i].name,&stu[i].score[0],&stu[i].score[1],&stu[i].score[2]);   // 输入数据  
		stu[i].aver=(stu[i].score[0]+stu[i].score[1]+stu[i].score[2])/3.0;                                    // 求各人平均成绩  
	}
}

struct student max(struct student stu[])          // 定义max 函数  
{
	int i,m=0;                                       // 用m存放成绩最高的学生在数组中的序号  
	for(i=0;i<N;i++)
	if (stu[i].aver>stu[m].aver) m=i;            // 找出平均成绩最高的学生在数组中的序号    
	return stu[m];                                  // 返回包含该生信息的结构体元素  
}

void print(struct student stud)                   // 定义print函数  
{ 
	printf("\n成绩最高的学生是:\n");
	printf("学号:%d\n姓名:%s\n三门课成绩:%5.1f,%5.1f,%5.1f\n平均成绩:%6.2f\n",stud.num,stud.name,stud.score[0],stud.score[1],stud.score[2],stud.aver);
}

在这里插入图片描述

9.4用指针处理链表

  • 链表概述
    链表是一种常见的重要的数据结构,是动态地进行存储分配的一种结构
    可以根据需要看开辟内存单元
    在这里插入图片描述
  • 链表头指针,在图中以head表示
  • 链表中每一个元素称为“结点”,包含两个部分:
    1. 用户需要用的实际数据
    2. 下一个结点的地址
      最后一个元素不再指向其他元素,它称为“表尾”,他的地址部分存放空指针“NULL”

9-8静态链表 的建立

用结构体变量来建立链表是最合适的

在这里插入图片描述在这里插入图片描述

#include <stdio.h>
struct student                                  // 声明结构体类型struct student  
{
	int num;
	float score;
	struct student *next;
};

int main()
{
	struct student a,b,c,*head,*p;               // 定义3个结构体变量作为链表的结点  
	a. num=10101; a.score=89.5;                  // 对结点a的num和score成员赋值  
	b. num=10103; b.score=90;                    // 对结点b的num和score成员赋值       
	c. num=10107; c.score=85;                    // 对结点c的num和score成员赋值  
	head=&a;                                     // 将结点a的起始地址赋给头指针head  
	a.next=&b;                                   // 将结点b的起始地址赋给a结点的next成员  
	b.next=&c;                                   // 将结点c的起始地址赋给a结点的next成员  
	c.next=NULL;                                 // c结点的next成员不存放其他结点地址  
	p=head;                                      // 使p也指向a结点  
	do        
	{
		printf("%ld %5.1f\n",p->num,p->score);    // 输出p指向的结点的数据  
		p=p->next;                                // 使p指向下一结点  
	}while(p!=NULL);                           	// 输出完c结点后p的值为NULL,循环终止  
	
	return 0;
}

在这里插入图片描述
本程序中所有节点都是在程序中定义的,不存在临时开辟的,用完后也不能释放,这种链表称为“静态链表”

9-9 动态 链表的建立

  • 概述
    动态链表:指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟节点和输入各节点的数据,并建立起前后相链的结构

在这里插入图片描述
请参考《C程序设计(第四版)》P311

#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct student)
struct student
{
	long num;
	float score;
	struct student *next;
};
int n;  

struct student *creat(void)   
{
	struct student *head;
	struct student *p1,*p2;
	n=0;

	p1=p2=(struct student *)malloc(LEN);
	scanf("%ld,%f",&p1->num,&p1->score);
	head=NULL;
	
	while(p1->num!=0)
	{
		n=n+1;
		if(n==1)
			head=p1;
		else 
			p2->next=p1;
		
		p2=p1;
		p1=(struct student*)malloc(LEN);
		scanf("%ld,%f",&p1->num,&p1->score);
	}
	p2->next=NULL;
	return(head);
}

int main()
{ 
	struct student *pt;
	pt=creat();                                                // 函数返回链表第一个结点的地址 
	printf("\nnum:%ld\nscore:%5.1f\n",pt->num,pt->score);      // 输出第一个结点的成员值
	return 0;
};

在这里插入图片描述

9-10输出链表

将链表各节点的数据依次输出
在这里插入图片描述

#include <stdio.h>
#include <malloc.h>
#define LEN sizeof(struct student)
struct student
{
	long num;
	float score;      
	struct student *next;
};
int n; 

struct student *creat()
{
	struct student *head;
	struct student *p1,*p2;
	n=0;
	p1=p2=( struct student*) malloc(LEN);
	scanf("%ld,%f",&p1->num,&p1->score);
	head=NULL;
	while(p1->num!=0)
	{
		n=n+1;
		if(n==1)
			head=p1;
		else 
			p2->next=p1;
			
		p2=p1;
		p1=(struct student*)malloc(LEN);
		scanf("%ld,%f",&p1->num,&p1->score);
	}
	p2->next=NULL;
	return(head);
}

void print(struct student *head)
{
	struct student *p;
	printf("\nNow,These %d records are:\n",n); 
	p=head;
	if(head!=NULL)
	do{
		printf("%ld %5.1f\n",p->num,p->score);
		p=p->next;
	}while(p!=NULL);
}

int main()
{
	struct student *head   ;
	head=creat();
	print(head);     
	return 0;
}

在这里插入图片描述

9.5共用体类型

  • 概述
    用同一段内存单元存放不同类型的变量。
    在这里插入图片描述

  • 定义共用体类型变量的形式
    union 共用体名
    {
    成员表列
    }变量表列;

    定义“共用体” 和 “结构体”的形式相似,但它们含义不同

    其他书籍中将“共用体”翻译为“联合体”,但是不方便理解

  • 引用共用体变量的方式
    不建议引用共用体变量,建议直接引用成员变量。

  • 共用体数据类型的特点

    1. 共用体初始化
      只能有一个初始化常量 在这里插入图片描述
      1. 不能对共用体变量名赋值,也不能企图引用变量名来得到一个值
        在这里插入图片描述
      2. 以前C规定不能把共用体变量作为函数参数,但可以使用指向共用体变量的指作函数参数
        C99 允许 共用体变量作为函数参数
      3. 共用体 可以出现在结构体定义中,可以定义共用体数组
        结构体 也可以出现在共用体 定义中
        数组也可以作为共用体的成员

9-11共用体使用例子

在这里插入图片描述

#include <stdio.h>
struct
{
	int num;
	char name[10];
	char sex;
	char job;
	union
	{
		int clas;
		char position[10];
	}category;
}person[2];

int main()
{
	int i;
	for(i=0;i<2;i++)
	{
		printf("please enter the data of person:\n");
		scanf("%d %s %c %c", &person[i].num, &person[i].name, &person[i].sex, &person[i].job);
		if(person[i].job == 's')
			scanf("%d", &person[i].category.clas);
		else if(person[i].job == 't')
			scanf("%s", person[i].category.position);
		else
			printf("Input error!");
	}
	printf("\n");
	printf("No.  name      sex job class/position\n");
	for(i=0;i<2;i++)
	{
		if (person[i].job == 's')
			printf("%-6d%-10s%-4c%-4c%-10d\n",person[i].num, person[i].name,
			person[i].sex, person[i].job, person[i].category.clas);
		else
			printf("%-6d%-10s%-4c%-4c%-10s\n",person[i].num, person[i].name,
			person[i].sex, person[i].job, person[i].category.position);
	}
	return 0;
}



在这里插入图片描述

9.6 使用枚举类型

  • 概述;
    • 如果变量只有几个可能的值,则可以定义为枚举类型 ,把可能的值一一列举出来,变量的值只限于列举出来的值的范围。
  • 形式
    • enum [枚举名] {枚举元素列表};
    • 举例
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 说明
      1. 枚举元素按常量处理,,故称为枚举常量,不可对其赋值。
      2. 枚举元素代表一个整数
        默认值为 0,1,2,3,4,5…
        也可以人为指定数值在这里插入图片描述
        在这里插入图片描述
        可以引用输出在这里插入图片描述
        C99 把 枚举类型 作为整型数据的一种,用户自定义的整型数据
      3. 枚举元素可以用来作判断比较
        在这里插入图片描述

9-12 枚举类型使用举例

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
int main()
{
	enum Color {red,yellow,blue,white,black};         //声明枚举类型enum Color
	enum Color i,j,k,pri;                             //定义枚举变量i,j,k,pri
	int n,loop;
	n=0;
	for (i=red;i<=black;i++)                          //外循环使i的值从red变到black
	{		
		for (j=red;j<=black;j++)                        //中循环使j的值从red变到black
		{	if (i!=j)                                     //如果二球不同色
			{ 
				for (k=red;k<=black;k++)                  //內循环使k的值从red变到black
				{ 
					if ((k!=i) && (k!=j))                      //如果3球不同色
					{
						n=n+1;                                 //符合条件的次数加1
						printf("%-4d",n);                      //输出当前是第几个符合条件的组合
						for (loop=1;loop<=3;loop++)            //先后对三个球分别处理
						{
							switch (loop)                       //loop的值从1变到3
							{
								case 1: pri=i;break;             //loop的值为1时,把第1球的颜色赋给pri      
								case 2: pri=j;break;             //loop的值为2时,把第2球的颜色赋给pri     
								case 3: pri=k;break;             //loop的值为3时,把第3球的颜色赋给pri    
								default:break;
							}
							
							switch (pri)                        //根据球的颜色输出相应的文字
							{
								case red:printf("%-10s","red"); break;            //pri的值等于枚举常量red时输出“red”
								case yellow: printf("%-10s","yellow"); break;     //pri的值等于枚举常量yellowd时输出“yellow”
								case blue: printf("%-10s","blue"); break;         //pri的值等于枚举常量blue时输出“blue” 
								case white: printf("%-10s","white"); break;       //pri的值等于枚举常量white时输出“white”
								case black: printf("%-10s","black"); break;       //pri的值等于枚举常量black时输出“black”
								default :break;
							}
						}
						printf("\n");
					}
				}			
			}
		}
	}
	printf("\ntotal:%5d\n",n);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

9.7 用typedef 声明新类型名

  • 概述:

    • 除了直接使用C提供的
      • 标准类型名(如:int char float double long)
      • 结构体
      • 共用体
      • 枚举类型
        还可以用typedef 指定新的类型名来代替已有的类型名
  • 使用举例

    1. 简单地用一个新的类型名代替原有的类型名
      typedef int Integer;
      typedef float Real;
      则以下等价:
      int i,j; float a,b;
      Integer i,j; Real a,b;

    FORTRAN 语言中Integer Real,以适用他们的编程习惯
    在这里插入图片描述

    1. 命名一个简单的类型名代替复杂的类型表示方法
      在这里插入图片描述

      • 黄:指针
      • 蓝:具有10 个指针元素数组
      • 红:返回值为整型指针函数
        组合:一个指向数组的指针,数组具有 10个指向 返回值为整型指针的函数 指针

      (1)代表结构体类型
      在这里插入图片描述
      (2)代表数组类型
      typedef int Num [10]; //声明 Num为整型数组类型名
      Num a; //定义 a 为整形数组名 Num 是 int[10] 同义词
      (3)代表指针
      typedef char * String; //声明a为字符指针类型
      String p , s[10]; //p 字符指针变量 s 为字符指针数组 String是 char * 同义词
      (4)代表指向函数的指针类型
      typedef int ( * Pointer ) (); //声明Pointer为指向函数的指针变量
      Pointer p1,p2; //p1 p2 为Pointer类型的指针变量 Pointer 是 int (*) () 同义词
      在这里插入图片描述
      习惯将声明的类型名的
      首字母设为 大写
      ,以便与系统提供的标准类型标志符相区别

    • typedef 与 #define 比较
      typedef int Coount ; //在编译阶段处理
      #define Count int //只是在预编译过程中作简单的字符串替换

    • 当不同源文件用到同一类型数据时,(数组 指针 结构体 共用体 ),可以将 typedef 放在一个头文件中,然后在需要用的源文件中,用#include指令把它们包含在文件中

    • 使用typedef有利于程序的通用与移植
      不同硬件int有不同的字节长度定义,有2字节 和 4字节 的区别
      当从4字节系统中 移植 到 2字节系统中,一般方式是将int 替换成 long ,int a,b,c --> long a,b,c,需要对程序多出进行修改
      可以用 typedef int Integer;
      typedef long Integer;

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值