C语言个人学习笔记 第九章:复杂数据类型与结构体

C语言萌新个人笔记~~ 第九章:复杂数据类型与结构体

《程序设计基础(C语言)》高克宁 等 编著

个人学习笔记

9.1概述

C语言中引入结构体(struct)类型,对描述对象的一个或多个特征进行封装。如:
struct student
{
	char id[8];
	char name[8];
	int age;
	char sex;
};

利用已经完成定义的结构体类型所声明的变量成为结构体变量。

struct student stu1,stu2;/*stu1和stu2为结构体变量*/

为结构体变量赋值:

strcpy(stu1.id,"16001");
stu1.sex='m';

为了节省空间并保证特征描述的准确性,C语言中引入共用体,实现成员共享一段内存空间,即允许多个不在同一时期使用的不同类型的变量共享同一存储空间。例如:

struct member
{
	union
	{long employeeID;
	 char studentID[8];
	}id;
	char mane[10];
	int age;
	char sex;
};

9.2结构体类型

C语言要求先定义结构体类型,再声明该类型的变量。

9.2.1结构体类型定义

定义格式
struct 结构体类型或标识符
{
成员变量列表;
};
定义新的结构体类型时,其成员变量的类型可以是已定义的结构体类型。

struct point
{
	float x;
	float y;
	float z;
};
struct line
{
	struct point startpoint;
	struct point endpoint;
};	

注意:
(1)结构体类型名为struct 和标识符组合而成(struct class、struct line ),class和line不是结构体类型名;

struct class c1,c2;//声明struct class型的变量

(2)成员变量名具有唯一性
(3)C语言允许定义指向相同结构体类型的指针成员,但不允许嵌套定义相同类型的成员变量

struct point
{
	struct point *p1;//正确
	struct point p2;//错误
}

(4)结构体类型的有效范围与其在源程序中的定义位置有关。

9.2.2结构体类型变量声明与初始化

1.结构体变量声明

三种声明方式
(1)先定义结构体类型,后变量声明;

/*已定义结构体类型struct student*/
struct student stu1;/*声明变量stu1*/

(2)定义结构体类型的同时声明变量

struct student
{
   char sex;
   int id;
}stu1,stu2,stu3;

(3)直接声明结构体类型变量
无结构体类型标识符,无法在其他场合声明变量,通常用于定义复杂的结构体类型

struct stuRec
{
	char id[8];
	char name[8];
	char sex;
	struct    /*无结构体类型标识符定义方式*/
	{
		int year;
		int month;
		int day;
	}birthday;
	float sEn;sCpn;sumScore;
}stu1;

系统对结构体变量所分配的内从空间要依存系统的内存对齐原则;

2.结构体变量初始化

结构体类型变量的初始化3种方式
(1)
结构体类型名 变量名={成员1初值,成员2初值,······,成员n初值};

struct student stu1={"16011","李想",18,"f"};

(2)和(3)

(2)
struct student
{
	char id[8];
	char name[8];
	int age;
	char sex;
}stu1={"16011","李想",18,"f"};
(3)
struct 
{
	char id[8];
	char name[8];
	int age;
	char sex;
}stu1={"16011","李想",18,"f"};

初始化时允许只对部分变量初始化,但是要求初始化的数据必须至少有一个;

struct point 
{
	float x;
	float y;
	float z;
}p1={1};
/*初始化结果:x为1,y和z为0;*/

未被赋值成员自动默认赋值:int为0,char为‘\0‘,float和double为0.0,char型数组为" ", int 型数组{0};
复杂结构体类型的初始化同样遵循上述规律。

struct line
{
	struct point startpoint;
	struct point endpoint;
}line1={{0,0,0}.{100,0,0}};

9.2.3结构体变量的引用

1.结构体成员变量的引用

通过运算符“.”和成员变量名实现引用:结构体类型变量.成员变量。

struct student stu1;
gets(stu1.id);
gets(stu1.name);
scanf("%d",&stu1.age);
scanf("%d",&stu1.sex);
printf("%s %s %c %d\n",stu1.id,stu1.name,stu1.age,stu1,sex);

复杂类型的结构体使用“.”逐层访问
sizeof得到内存空间字节数:

sizeofstruct student)
sizeof(stu1)/*两者等价,都为24B*/
struct date   /*无结构体类型标识符定义方式*/
{
	int year;
	int month;
	int day;
};
struct stuRec
{
	char id[8];
	char name[8];
	struct date birthday;
	char sex;
	float sEn;sCpn;sumScore;
}stu1;
stuRec.birthday.year=2000;
stuRec.birthday.month=10;
2.结构体变量的引用

结构体变量支持运算符“.”、&、sizeof以及赋值运算。

struct stuRec stu1,stu2,stu3={"16001","李想",2001,11,21,'f',81.5,91,172.5};
stu1=stu3;
stu2.birthday=stu1.birthday;

9.2.4结构体数组

结构体数组是一组结构体类型的数据集合,每个数组元素都是相同类型的变量。

1.结构体数组的定义

三种方式

1struct student
{
	char id[8];
	char name[8];
	int age;
	char sex;
};
struct student stu[3];2struct student
{
	char id[8];
	char name[8];
	int age;
	char sex;
}stu[3];3struct
{
	char id[8];
	char name[8];
	int age;
	char sex;
}stu[3];
2.结构体数组的储存

计算公式
数组元素个数*sizeof(结构体类型)

3.结构体数组初始化
struct student stu[3]={{"16011","李想",18,'f'},{"16012","杨想",19,'f'},{"16013","想",20,'f'}};

结构体数组初始化可省略数组长度,系统根据初始化数据的个数确定数组长度。

struct keyboard
{
	char word[20];
	int count;
}keywords[]={{"break",0},{"break",0},{"break",0},{"break",0}};
4.结构体数组的引用

分为对数组元素的引用和对数组整体的引用
引用数组元素:

struct student str[3];
strcpy(stu[0].name,"lixiang");
strcpy(stu[1].name,"yanghaiyue");
stu[1].age=19;
stu[2].sex='f';

将结构体数组整体引用:
(1)作为函数参数。结构体数组首地址由主调函数传递给被调函数
(2)作为一块连续存储单元的起始地址与结构体类型指针变量配合使用

9.2.5结构体与函数

功能
(1)结构体变量作为函数参数传值
(2)结构体数组作为函数参数传址
(3)函数声明为结构体类型,则其返回值为结构体类型变量

9.2.6结构体类型指针

当系统为结构体类型的数组或变量在内存中分配一段存储单元后,通过数组名或取变量地址运算符(&)可获得这段连续单元的起始地址,

1.结构体类型指针变量的声明与初始化

结构体类型名 *结构体类型指针变量名;
(1)结构体类型指针变量可以用于指向相同类型的结构体变量或数组

struct student stu={"16011","李想",18,'f'};
struct student *ps;
ps=&stu;

(2)结构体指针也可以在定义的同时初始化

struct student stu[3];
struct student *ps=stu;
2.结构体类型指针变量的引用

主要运算符:"." “->” “*”
其中,指向运算符->只能用于结构体类型指针对成员的引用。
格式:
(*结构体类型指针变量).成员变量
结构体类型指针变量->成员变量
两种引用格式等价

struct student stu,*ps;
ps=&stu;
gets(stu,id);
gets(ps->name);
(*ps).age=18;
(*ps).sex='f';

结构体指针运算符还包括++、–、+、-、!等

struct student stu[3].*ps=stu;
ps++;/*现在指向stu【1】*/
(++ps)->age=18;/*++ps;ps->age=18*/
++ps->age/*stu[2]元素的age成员+1,即++(ps->age)*/
->运算符的使用与理解,与“.”的区别
struct date
{
	int a,b,c;
}A={1,2,3};
struct date *p;
p=&A;
int x,y;
x=p->a/*取出p所指向的结构体A中包含的成员a赋值给x*/
y=*p).a;/*与前一句等价*/

->为获取指针所指结构体成员的值,运算符->是运算符*和运算符.的结合

9.3共用体

共用体可以将不同类型的变量存放在同一内存区域内,使所用成员变量存放在同一段内存中,即共用体在任何时候只能有一个值,属于某一个数据成员。

1.共用体的类型定义

union variant
{
	int iVal;
	double dVal;
};

共用体类型名为union variant,两个成员分别为int型和double型。

2.共用体变量的声明

三种声明方式

union variant varData;

union variant
{
	int iVal;
	double dVal;
}varDate;

union
{
	int iVal;
	double dVal;
}varData;

系统按照varData中最大成员分配8B的存储空间,各个成员变量同时共用同一段内存空间。

3.共用体变量的赋值和引用

格式:
共用体变量.成员变量
共用体指针变量->成员变量

union variant varData,*pv;
pv=&varData;
varData.iVal=100;
pv->dVal=92;

9.4枚举类型

C语言提供了枚举(enum)类型,为具有少量可列举状态的变量定义一种新的数据类型。其中的少量变量为同一类型,能作为一个完整的逻辑整体。

1.枚举类型的定义

格式:enum 枚举标识符{常量列表}
enum为系统关键字;常量列表中每个数据成为枚举常量

enum direction{up,down.before,back,left,right};

通常情况下,系统自动为每一个枚举常量设定一个对应的整数常量值(从0开始)。
也可以自己定义:

enum direction{up=1,down=2.before=3,back=4,left=5,right=6};

允许枚举常量部分设定一个对应的整数常量值:`

enum direction{up=7,down=1.before,back,left,right};
/*从down后每一个整数值加一,即before,back,left,right分别为2,3,4,5*/

2.枚举变量的声明和引用

枚举类型主要用于描述特定的集合对象。

enum direction commands[8]={forword,back.left,right,right,right,forward,back};

把enum direction和int类比
int描述-231~231-1
enum direction描述0~5

9.5类型重定义

C语言提供关键字typedef实现对类型的重定义
格式:typedef 类型名称 类型标识符

typedef unsigned int COUNT;

typedef主要用法
(1)为基本数据类型定义新的类型名
(2)为自定义数据类型(结构体,共用体和枚举类型)定义简洁的类型名称,新定义的必须在}和;之间

typedef struct 
{
	double x;
	double y;
	double z;
}Point;

(3)为数组定义简洁的类型名称(数组也是一种特殊的数据类型)

typedef int INT_ARRAY_10[10];
INT_ARRAY_10 a,b,c;

(4)为指针定义简介的名称
可以为指针变量或函数指针定义新名称

typedef char * STRING;
STRING name={"lixing"};
typedef int (*FUN)(int a,int b);

typedef构造某个数据类型的新名字
#define只是简单的文本替换

9.6日期和语言

C语言的日期和时间函数定义在函数库time.h中,调用函数时应包含#include <time.h>宏命令。常用的时间和日期函数如下:

time_t time(time_t *t);
struct tm *gmtime(const time_t *timep);
char *ctime(const time_t *timep);
char *asctime(const struct tm *timep);

9.7链表(重点)

9.7.1链表定义

包含两部分:(1)实际存储的数据,数据域(2)下一个结点的地址,指针域

struct ListNode
{
	double date;
	struct ListNode *next;
};
struct ListNode *head;

9.7.2动态内存管理函数

C语言通过库函数实现动态内存管理,调用动态内存管理函数需要使用预编译指令#include 包含的头文件stdlib.h。

1.动态内存分配函数malloc

声明格式:

void *malloc(unsigned size);

malloc函数用于向内存申请size个字节的连续存储空间。
申请成功,返回一个指向该存储空间起始地址的指针;
申请失败,返回空指针NULL。
C语言规定:void*类型可以被强制转化为任何其他类型的指针。malloc函数分配的内存空间为经过初始化,直接使用这块内存中的值没有意义。

char *pc;
int *pi;
pc=malloc(100);
pi=malloc(50*sizeof(int));

在调用malloc函数后,需要检查其返回值,以确定指针是否有效。

if((pi=malloc(50*sizeof(50*sizeof(int)))==NULL)
{
	printf("内存分配失败");
	exit(1);
}
2.连续内存动态分配函数calloc

声明格式:

void *calloc(unsign n,unsigned size);

calloc函数用于申请n个连续的内存空间,每个存储空间的长度为size字节,并且将所分配的内存空间初始化为0。
申请成功:返回起始地址指针;若失败:返回空指针NULL

int *Pi;
if((pi=calloc(50,sizeof(int)))==NULL)
{
	printf("内存分配失败");
	exit(1);
}
3.内存分配调整函数realloc

声明格式:

void *realloc(void *ptr,unsign size);

realloc函数用于变更已分配内存空间的大小。realloc可以将ptr所指向的内存空间扩展或缩小为size大小。变更成功后返回调整后的内存空间的首地址。
内存扩展,原有内容不变,新增内存空间清零。
内存缩小,被缩小部分丢失。

int *p=(int *)malloc(sizeof(int)*10);
p=(int *)realloc(p,sizeof(int)*15);/*内存扩展*/
p=(int *)realloc(p,sizeof(int)*5);/*内存缩小*/
4.动态内存释放函数free

声明格式:

void free(void *ptr);

free函数用来释放由动态内存分配函数所分配的内存空间。参数ptr指向将被释放的内存空间首地址。

int *p=(int *)malloc(sizeof(int)*1);
*p=100;
···
free(p);
p=NULL;

9.7.3链表的基本操作

1.定义结点类型
typeof struct stuNode
{
	char id[8];
	char name[8];
	flaot sEn,sCpu;
	struct stuNode *next;
}stuNode,*stuNodeP;/*重定义为两个新类型,分别是结构体类型stuNode和结构体指针类型stuNodeP*/
stuNodeP headStu;//头指针
2.创建链表
typeof struct stuNode
{
	char id[8];
	char name[8];
	flaot sEn,sCpu;
	struct stuNode *next;
}stuNode,*stuNodeP;

#define LEN sizeof(stuNode);

stuNodeP Create_StuList(int n)
{
	int i;
	stuNodeP headStu;
	stuNodeP p0,p1;
	headStu=p0=(stuNode *)malloc(LEN);

	for(i=0;i<n;i++)
	{
		p1=(stuNode *)malloc(LEN);
		scanf("%s%s",p1->id,p1->name);
		scanf("%f%f",&p1->sEn,&p1->sCpu);
		p0->next=p1;
		p0=p1;
	}
	p1->next=NULL;
	return headStu;
}
3.遍历链表
void ListStu (stuNodeP headStu);
{
	int num;
	stuNodeP *p;
	p=headStu->next;
	while(p!=NULL)
	{
		printf("%d:%s %s %.lf %.lf\n",++num.p->id,p->name,p->sEn,p->sCpu);
		p=p->next;
	}
}
4.列表排序

交换过程只需要交换结点数据域的信息,其指针域内容不进行交换,即链表的整体逻辑结构不变。

stuNodeP Sort_StuList(stuNodeP headStu)
{
	stuNodeP p,q,min;
	char tid[8],tname[8];
	float tsEn,stCpu;
	for(p=heakStu->next;p->next!=NULL;p=p->next)
	{
		min=p;
		for(q=p->next;q;q=q->next)
			if(strcmp(q->id,min->id)<0)
				min=q;
		if(min!=p)
		{
			strcpy(tid,p->id); strcpy(p->id,min->id);
			strcpy(min->id,tid); strcpy(tname,p->name);
			tsEn=p->sEn; p->sEn=min->sEn; min->sEn=tsEn;
			tsCpu=p->sCpu; p->sCpu=min->sCpu; min->sCpu=tsCpu;
		}
	}
	return headStu;
}
5.在有序链表中插入结点
stuNodeP Insert_StuNode(stuNodeP headStu, char newId[8])
{
	stuNodeP p=headStu,q=headStu->next;
	stuNodeP newStuNode;
	while((strcmp(q->id,new)<0)&&(q->next!=NULL))
	{
		p=p->next;
		q=q->next;
	}
	newStuNode=(stuNode *)malloc(LEN);
	/*创建新结点并插入此结点*/
	strcpy(newStuNode->id,newId);
	scanf("%s%f%f",newStuNode->name,&newStuNode->sEn,&newStuNode->sCpu);
	if(strcmp(q->id,newId)<0) p=q;
	newStuNode->next=p->next;
	p->next=newStuNode;
	return headStu;
}	
6.删除指定结点
stuNodeP Delete_StuNode(stuNodeP headStu,char delId[8])
{
	stuNodeP lp,p;
	lp=headStu;
	p=lp->next;
	while((strcmp(p->id!=0))&&(p->next!=NULL))
	{
		lp=p;
		p=p->next;
	}
	if((strcmp(p-id,delId)==0)
	{
		lp->next=p->next;
		free(p);
	}
	else
	{
		printf("failed to find this student,failed to delete");
	return headStu;
}
7.链表基本操作综合使用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 10

//定义结构体类型
typeof struct stuNode
{
	char id[8];
	char name[8];
	flaot sEn,sCpu;
	struct stuNode *next;
}stuNode,*stuNodeP;/*重定义为两个新类型,分别是结构体类型stuNode和结构体指针类型stuNodeP*/
stuNodeP head;//头指针

//create a new chain table
stuNodeP Create_StuList(int n)
{
	int i;
	stuNodeP headStu;
	stuNodeP p0,p1;
	headStu=p0=(stuNode *)malloc(LEN);
	for(i=0;i<n;i++)
	{
		p1=(stuNode *)malloc(LEN);
		scanf("%s%s",p1->id,p1->name);
		scanf("%f%f",&p1->sEn,&p1->sCpu);
		p0->next=p1;
		p0=p1;
	}
	p1->next=NULL;
	return headStu;
}

//travserse the chain table
void ListStu (stuNodeP headStu);
{
	int num;
	stuNodeP *p;
	p=headStu->next;
	while(p!=NULL)
	{
		printf("%d:%s %s %.lf %.lf\n",++num.p->id,p->name,p->sEn,p->sCpu);
		p=p->next;
	}
}

//sort the chain table
stuNodeP Sort_StuList(stuNodeP headStu)
{
	stuNodeP p,q,min;
	char tid[8],tname[8];
	float tsEn,stCpu;
	for(p=heakStu->next;p->next!=NULL;p=p->next)
	{
		min=p;
		for(q=p->next;q;q=q->next)
			if(strcmp(q->id,min->id)<0)
				min=q;
		if(min!=p)
		{
			strcpy(tid,p->id); strcpy(p->id,min->id);
			strcpy(min->id,tid); strcpy(tname,p->name);
			tsEn=p->sEn; p->sEn=min->sEn; min->sEn=tsEn;
			tsCpu=p->sCpu; p->sCpu=min->sCpu; min->sCpu=tsCpu;
		}
	}
	return headStu;
}

//intsert nodes in an ordered chain table
stuNodeP Insert_StuNode(stuNodeP headStu, char newId[8])
{
	stuNodeP p=headStu,q=headStu->next;
	stuNodeP newStuNode;
	while((strcmp(q->id,new)<0)&&(q->next!=NULL))
	{
		p=p->next;
		q=q->next;
	}
	newStuNode=(stuNode *)malloc(LEN);
	/*创建新结点并插入此结点*/
	strcpy(newStuNode->id,newId);
	scanf("%s%f%f",newStuNode->name,&newStuNode->sEn,&newStuNode->sCpu);
	if(strcmp(q->id,newId)<0) p=q;
	newStuNode->next=p->next;
	p->next=newStuNode;
	return headStu;
}	

//delete a specified node
stuNodeP Delete_StuNode(stuNodeP headStu,char delId[8])
{
	stuNodeP lp,p;
	lp=headStu;
	p=lp->next;
	while((strcmp(p->id!=0))&&(p->next!=NULL))
	{
		lp=p;
		p=p->next;
	}
	if((strcmp(p-id,delId)==0)
	{
		lp->next=p->next;
		free(p);
	}
	else
	{
		printf("failed to find this student,failed to delete");
	return headStu;
}

//main function
int main()
{
	char id[8];
	head=Create_StuList(N);
	head=Sort_StuNode(head);
	ListStu(head);
	printf("please input the imformation of the student you want to insert\n");
	scanf("%s",id);
	head=Insert_StuNode(head,id);
	ListStu(head);
	printf("please input the imformation of the student you want to delete\n");
	scanf("%s",id);
	head=Delete_StuNode(head,id);
	ListStu(head);
	return 0;
}

9.8结构体新特性

C99标准在结构体部分增加了动态数组成员及指定成员初始化等特性

1.动态数组成员

typeof struct
{
	char id[8];
	char name[8];
	float s[];//动态数组成员,用于保存学生多门课程成绩
}Student;

注意,sizeof(student)只计算前两个成员所占空间大小

Student *p;
int n;
printf("please input the number of the course");
scanf("%d",&n);
p=(Student *)malloc(sizeof(Student)+n*sizeof(float));

2.指定成员初始化

typeof struct
{
	char id[8];
	char name[8];
	float sEn,sCpu;
}Student;
Student stu1={.sEn=90, .sCpu=87, .id="16021"};

如是结构体数组,可采取以下方式:

Student stu[3]={[1].id="16011",[2].id="16012"};

3.匿名成员

#include<stdio.h>
struct num
{
	int k;
	union//匿名共用体
	{
		struct//匿名结构体
		{
			int i,j;
		}struct
		{
			long m,n;
		}w;
	};
};
int main()
{
	struct mum n1;
	n1.k=10;
	n1.i=5;
	m1.w.n=12;
	printf("%d,%d,%ld\n",n1.k,n1.i,n1.w.n);
	return 0;
}

4.函数指针成员

结构体中的成员也可以是函数指针成员。

#include<stdio.h>
struct num
{
	int m,n;
	int (* fum)(int int );
};
int add(int m,int n)
{
	return m+n;
}
int mul(int m,int n)
{
	return m*n;
}
int mian()
{
	struct num mum1;
	num1.m=10; mum1.n=6;
	num.fun=add;
	printf("%d\n",num.fun(num1.m,num1.n));
	num.fun=mul;
	printf("%d\n",num1.fun(num1.m,num1.n));
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值