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得到内存空间字节数:
sizeof(struct 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.结构体数组的定义
三种方式
(1)
struct student
{
char id[8];
char name[8];
int age;
char sex;
};
struct student stu[3];
(2)
struct student
{
char id[8];
char name[8];
int age;
char sex;
}stu[3];
(3)
struct
{
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;
}