目录
1、初识结构体
(1)、为什么要使用结构体
在我们平常应用时我们会需要用和多类型数据表示一个整体,比如学生的信息。而我们平常使用的整型数,浮点型数,字符串都是分散的数据表示是。类比与数组的话,数组是元素类型一样的数据集合,如果是元素类型不同的数据集合,就需要用到结构体了
(2)、定义一个结构体
struct Student{
char name[32];
int age;
double score;
};//分号不能忘,不然会报错
它算是一个模板,一般不给赋具体的值,因为每一项在实际应用中并不是都要使用,所以一般在使用时再给其赋具体的值
结构体中的每个成员都是结构体中的一个域,也称为域表或成员变量
我们可以在结构体声明的同时,定义结构体变量,但是尽量少用
结构体的使用示例:
#include <stdio.h>
#include <string.h>
struct Student{
char name[32];
int age;
char gender;
int student_number;
}stu2;
int main(){
struct Student stu1;
strcpy(stu1.name,"张三");//点运算符来访问结构体中的成员变量(域)
stu1.age=21;
stu1.gender='g';
stu1.student_number=20230101;
strcpy(stu2.name,"李四");
stu2.age=22;
stu2.gender='c';
stu2.student_number=20230102;
printf("姓名:%s 年龄:%d 性别:%c 学号:%d\n" ,stu1.name,stu1.age,stu1.gender,stu1.student_number);
printf("姓名:%s 年龄:%d 性别:%c 学号:%d\n" ,stu2.name,stu2.age,stu2.gender,stu2.student_number);
return 0;
}
运行结果:
重点认知:结构体没什么特殊的,只是把变量藏在结构体里面,而内部的变量,以前学习的东西是通用的,只是“触达的方式”不同
2、结构体数组
结构体数组其实和普通数组没区别,只是数组里存放的值是一个个结构体而已
接下来用代码来演示结构体数组的应用:
#include <stdio.h>
struct Student
{
//姓名
char name[24];
//性别
char gender[4];
//年龄
int age;
//成绩
int grade;
//地址
char address[24];
};
int main(){
//结构体和数组的组合
struct Student str[]={
{"张三","男",19,78,"南昌"},
{"李四","男",20,99,"赣州"},
{"王二狗","女",18,38,"吉安"}
};
int len = sizeof(str)/sizeof(str[0]);
for(int i=0;i<len;i++){
printf("姓名:%s 性别:%s 年龄:%d成绩:%d 地址:%s\n",
str[i].name,str[i].gender,str[i].age,str[i].grade,str[i].address);
}
return 0;
}
运行结果
案例:选民系统
#include <stdio.h>
#include <string.h>
struct Xuanmin{
char name[32];
int tickets;
};
int main(){
//先统计现场人数
int renshu =0;
int value1=0;
int value2=0;
printf("请主持人确定一下现场人数");
scanf("%d",&renshu);
printf("现场参加投票的一共有%d人\n",renshu);
//1初始化选民信息
struct Xuanmin join[3];//参选人数
int len = sizeof(join)/sizeof(join[0]);
for(int i=0;i<len;i++){
printf("请输入第%d位选民姓名:",i+1);
scanf("%s",join[i].name);
}
//2、唱票环节
printf("参选人已经确定,下面开始唱票环节\n");
for(int i=0;i<len;i++){
join[i].tickets=0;
}
char support[24];
for(int i=0;i<renshu;i++){
value1=0;
memset(support,'\0',sizeof(support));
printf("请输入你支持参选人的姓名:");
scanf("%s",&support);
for(int j=0;j<len;j++){
//printf("%s",join[j].name);
if(strcmp(support,join[j].name)==0){
join[j].tickets++;
value1=1;
//break;
}
}
if(value1==0){
printf("参选人中没有此人,系统认定您位弃票\n");
value2++;
}
}
printf("投票结束\n");
//3、公布结果
printf("现在公布结果\n");
for(int i=0;i<len;i++){
printf("姓名:%s 票数:%d\n",join[i].name,join[i].tickets);
}
struct Xuanmin max=join[0];
if(join[0].tickets==join[1].tickets==join[2].tickets){
printf("三人平票\n");
}else{
for(int i=1;i<len;i++){
if(max.tickets<join[i].tickets){
max=join[i];
}
}
printf("票数最高的是:\n");
printf("姓名:%s 票数:%d\n",max.name,max.tickets);
}
printf("有%d人弃票",value2);
return 0;
}
3、结构体指针
在这里我们先回忆一下之前学习的指针内容
指针就是地址,指针变量就是存放地址的变量,在这里可以发现结构体也是变量,而变量的访问方式有两种:1、变量名访问 2、地址访问 ,我们上面用到的都是用变量名访问。
通过结构体变量地址来访问该结构体需要一个变量来保持这个地址:这和之前说的指针其实是一样的只是指针类型是结构体
通过结构体访问指针
#include <stdio.h>
struct Student
{
//姓名
char name[24];
//性别
char gender[4];
//年龄
int age;
//成绩
int grade;
//地址
char address[24];
};
int main(){
//结构体和数组的组合
struct Student str[]={
{"张三","男",19,78,"南昌"},
{"李四","男",20,99,"赣州"},
{"王五","女",18,38,"吉安"}
};
struct Student *p;
p=str;
int len = sizeof(str)/sizeof(str[0]);
for(int i=0;i<len;i++){
printf("姓名:%s 性别:%s 年龄:%d成绩:%d 地址:%s\n",
p->name,p->gender,p->age,p->grade,p->address);
p++;//指针偏移到结构体数组中的下一个结构体
}
p=str;//指针回归到结构体数组首地址
for(int i=0;i<len;i++){
printf("姓名:%s 性别:%s 年龄:%d成绩:%d 地址:%s\n",
p->name,p->gender,p->age,p->grade,p->address);
p++;//指针偏移到结构体数组中的下一个结构体
}
p=str;//指针回归到结构体数组首地址
for(int i=0;i<len;i++){
printf("姓名:%s 性别:%s 年龄:%d成绩:%d 地址:%s\n",
(p+i)->name,(p+i)->gender,(p+i)->age,(p+i)->grade,(p+i)->address);
//指针偏移到结构体数组中的下一个结构体
}
p=str;//指针回归到结构体数组首地址
for(int i=0;i<len;i++){
printf("姓名:%s 性别:%s 年龄:%d成绩:%d 地址:%s\n",
(p+i)->name,(p+i)->gender,(p+i)->age,(p+i)->grade,(p+i)->address);
//指针偏移到结构体数组中的下一个结构体
}
return 0;
}
运行结果
选民系统改进->使用指针实现
#include <stdio.h>
#include <string.h>
struct Xuanmin{
char name[32];
int tickets;
};
int main(){
//先统计现场人数
int renshu =0;
int value1=0;
int value2=0;
printf("请主持人确定一下现场人数");
scanf("%d",&renshu);
printf("现场参加投票的一共有%d人\n",renshu);
//1初始化选民信息
struct Xuanmin join[3];//参选人数
struct Xuanmin *p;
p=join;
int len = sizeof(join)/sizeof(join[0]);
for(int i=0;i<len;i++){
printf("请输入第%d位选民姓名:",i+1);
scanf("%s",p->name);
p++;
}
p=join;
//2、唱票环节
printf("参选人已经确定,下面开始唱票环节\n");
for(int i=0;i<len;i++){
p->tickets=0;
p++;
}
p=join;
char support[24];
for(int i=0;i<renshu;i++){
value1=0;
memset(support,'\0',sizeof(support));
printf("请输入你支持参选人的姓名:");
scanf("%s",&support);
for(int j=0;j<len;j++){
//printf("%s",join[j].name);
if(strcmp(support,p->name)==0){
p->tickets++;
value1=1;
//break;
}
p++;
}
p=join;
if(value1==0){
printf("参选人中没有此人,系统认定您位弃票\n");
value2++;
}
}
printf("投票结束\n");
//3、公布结果
printf("现在公布结果\n");
p=join;
for(int i=0;i<len;i++){
printf("姓名:%s 票数:%d\n",p->name,p->tickets);
p++;
}
p=join;
struct Xuanmin *max=p;
if((p+0)->tickets==(p+1)->tickets==(p+2)->tickets){
printf("三人平票\n");
}else{
for(int i=1;i<len;i++){
if(max->tickets<p->tickets){
max=p;
}
p++;
}
p=join;
printf("票数最高的是:\n");
printf("姓名:%s 票数:%d\n",max->name,max->tickets);
}
printf("有%d人弃票",value2);
return 0;
}
选民系统再改进 :结构体数组,指针,函数应用综合应用
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Xuanmin{
char name[32];
int tickets;
};
//确定选民人数,和投票人数,并初始化选民信息
struct Xuanmin* initXms(struct Xuanmin *p,int *len,int *lenshu){
printf("请输入选民人数:");
scanf("%d",len);
p=(struct Xuanmin*)malloc(sizeof(struct Xuanmin)*(*len));
printf("请主持人确定在场投票人数\n");
printf("在场投票人数有:");
scanf("%d",lenshu);
printf("下面开始录入参选人姓名\n");
for(int i=0;i<*len;i++){
printf("第%d位参选人姓名是:",i+1);
scanf("%s",p->name);
p->tickets=0;
p++;
}
return p-(*len);
}
//遍历选民信息
void Bianli(struct Xuanmin *p,int len){
for(int i=0;i<len;i++){
printf("姓名:%s 票数:%d\n",p->name,p->tickets);
p++;
}
}
//投票选举
struct Xuanmin* Toupiao(struct Xuanmin *p,int len,int lenshu ,int *value){
char mz[32];
memset(mz,'\0',sizeof(mz));
int value2=0;
for(int i=0;i<lenshu;i++){
value2=0;
printf("请输入你所支持的人的名字:");
scanf("%s",mz);
for(int j=0;j<len;j++){
if(strcmp(p->name,mz)==0){
p->tickets++;
value2=1;
}
p++;
}
if(value2==0){
printf("没有此人系统认定你为弃票!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
(*value)++;
}
p=p-len;
}
printf("value==%d\n",*value);
return p;
}
//公布结果
void XmMax(struct Xuanmin *p,int len){
struct Xuanmin *max;
max=p;
for(int i=0;i<len;i++){
if(max->tickets<p->tickets){
max=p;
}
p++;
}
p=p-len;
Bianli(max,1);
}
int main(){
struct Xuanmin *join=NULL;
int len=0;
int lenshu=0;
int value=0;//用来记录弃票数
join=initXms(join,&len,&lenshu);
Bianli(join,len);
printf("信息录入成功接下来开始投票环节\n");
Toupiao(join,len,lenshu,&value);
printf("投票结束,公布结果\n");
Bianli(join,len);
printf("共有%d人参与投票,有%d人弃票\n",lenshu,value);
printf("最终获选者是:\n");
XmMax(join,len);
return 0;
}
选民系统pro max版,使用二级指针
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Xuanmin{
char name[32];
int tickets;
};
//
void initXms(struct Xuanmin **p,int *len,int *lenshu){//二级指针保存的是指针变量的地址
printf("请输入选民人数:");
scanf("%d",len);
*p=(struct Xuanmin*)malloc(sizeof(struct Xuanmin)*(*len));
printf("请主持人确定在场投票人数\n");
printf("在场投票人数有:");
scanf("%d",lenshu);
printf("下面开始录入参选人姓名\n");
for(int i=0;i<*len;i++){
printf("第%d位参选人姓名是:",i+1);
scanf("%s",(*p)->name);
(*p)->tickets=0;
(*p)++;
}
*p= *p-(*len);
}
//遍历选民信息
void Bianli(struct Xuanmin *p,int len){
for(int i=0;i<len;i++){
printf("姓名:%s 票数:%d\n",p->name,p->tickets);
p++;
}
}
//投票选举
struct Xuanmin* Toupiao(struct Xuanmin *p,int len,int lenshu ,int *value){
char mz[32];
memset(mz,'\0',sizeof(mz));
int value2=0;
for(int i=0;i<lenshu;i++){
value2=0;
printf("请输入你所支持的人的名字:");
scanf("%s",mz);
for(int j=0;j<len;j++){
if(strcmp(p->name,mz)==0){
p->tickets++;
value2=1;
}
p++;
}
if(value2==0){
printf("没有此人系统认定你为弃票!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
(*value)++;
}
p=p-len;
}
printf("value==%d\n",*value);
return p;
}
//公布结果
void XmMax(struct Xuanmin *p,int len){
struct Xuanmin *max;
max=p;
for(int i=0;i<len;i++){
if(max->tickets<p->tickets){
max=p;
}
p++;
}
p=p-len;
Bianli(max,1);
}
int main(){
struct Xuanmin *join=NULL;
int len=0;
int lenshu=0;
int value=0;//用来记录弃票数
initXms(&join,&len,&lenshu);
Bianli(join,len);
printf("信息录入成功接下来开始投票环节\n");
Toupiao(join,len,lenshu,&value);
printf("投票结束,公布结果\n");
Bianli(join,len);
printf("共有%d人参与投票,有%d人弃票\n",lenshu,value);
printf("最终获选者是:\n");
XmMax(join,len);
return 0;
}
4、共用体/联合体
共用体就是不同类型变量共享同一块空间
结构体与共用体的区别:
1、结构体元素有各自单独空间,共用体元素共享空间,空间大小有最大类型确定
2、结构体元素互不影响而共用体赋值会导致覆盖
#include <stdio.h>
struct TestT
{
double ddate;
int idata;
char cdata;
};
union TestU
{
int idata;
char cdata;
double ddate;
};
int main(){
struct TestT t1;
union TestU u1;
printf("结构体t1的大小%d\n",sizeof(t1));
printf("结构体t1.idata的大小%d ,地址是%p\n",sizeof(t1.idata),&t1.idata);
printf("结构体t1.cdata的大小%d ,地址是%p\n",sizeof(t1.cdata),&t1.cdata);
printf("结构体t1.ddate的大小%d ,地址是%p\n",sizeof(t1.ddate),&t1.ddate);
//共用体中值的覆盖问题
u1.idata=19;
u1.cdata='c';
printf("u1.idata=%d\n",u1.idata);
printf("u1.cdata=%d\n",u1.cdata);
printf("u1.ddate=%d\n",u1.ddate);
printf("共用体u1的大小%d\n",sizeof(u1));
printf("共用体u1.idata的大小%d ,地址是%p\n",sizeof(u1.idata),&u1.idata);
printf("共用体u1.cdata的大小%d ,地址是%p\n",sizeof(u1.cdata),&u1.cdata);
printf("共用体u1.ddate的大小%d ,地址是%p\n",sizeof(u1.ddate),&u1.ddate);
return 0;
}
运行结果
结构体struct 的空间计算遵循的2个原则:
1:整体空间是占用空间最大的成员(的类型)所占字节数的整数倍,但是在32位Linix + gcc环境下,若最大成员类型所占字节数超过4,如double是8,则整体空间是4的倍数即可。
2:数据对齐原则。内存按结构体成员的先后顺序排列,当排到该成员变量时,其前面已摆放的空间大小必须是该成员类型大小的整数倍, 如果不够则补齐,依次向后类推。但在Linux + gcc环境下,某成员类型所占字节数超过4,如double是8,则前面已摆放的空间大小是4的整数倍即可,不够则补齐
共用体union元素是共享一个空间,空间大小有最大类型确定
结构体的应用:有若干个人员的数据其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。要求用同一个表格来处理
#include <stdio.h>
#include <string.h>
struct Tset
{
char name[24];
char gender;
char zhiye;
union C{//这里的c可写可不写
int class_and_grade;
char post[32];
}mes;//在声明时定义变量
};
int main(){
struct Tset ts[2];
struct Tset *p=NULL;
p=ts;
for(int i=0;i<2;){
printf("请输入你的职业:学生输入s,教师输入t\n");
scanf("%c",&(p->zhiye));//这里zhiye表示的是字符变量不是地址,所以p指向他的时候得取地址才能输入值
if(p->zhiye == 's'){
printf("同学你好请输入你的班级:\n");
scanf("%d",&(p->mes.class_and_grade));//这里和上面同理
printf("同学你好请输入你的姓名\n");
scanf("%s",p->name);//这里指向的是字符串数组,数组名就是地址,所以指向的就是name的地址
p++;
i++;
}else if(p->zhiye == 't'){
printf("老师您好请输入你的职务:\n");
scanf("%s",p->mes.post);
printf("老师你好请输入你的姓名\n");
scanf("%s",p->name);
p++;
i++;
}else{
printf("输入错误请输入正确的职业代码,谢谢!!!\n");
}
getchar();//吸收上面的回车
}
p=ts;
for(int i=0;i<2;i++){
if(p->zhiye == 's'){
printf("姓名:%s 职业:%c 班级:%d\n",p->name,p->zhiye,p->mes.class_and_grade);
p++;
}else{
printf("姓名:%s 职业:%c 职务:%s\n",p->name,p->zhiye,p->mes.post);
p++;
}
}
return 0;
}
运行结构
当然这个代码也可以和上面一样进行改进,将步骤封装成函数
5、枚举类型
枚举是 C 语言中的一种基本数据类型,用于定义一组具有离散值的常量。,它可以让数据更简洁,更易读。枚举类型通常用于为程序中的一组相关的常量取名字,以便于程序的可读性和维护性。定义一个枚举类型,需要使用 enum 关键字,后面跟着枚举类型的名称,以及用大括号 {} 括起来的一组枚举常量。每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从 0 开始递增。
枚举语法定义格式为:enum 枚举名 {枚举元素1,枚举元素2,……};
比如一周有七天我们用枚举的方式
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
注意:第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。枚举里面的值不能重复
枚举变量的定义
1、先定义枚举类型,在定义枚举变量
2、定义枚举类型的同时定义枚举变量
3、省略枚举名称,直接定义枚举变量
枚举在 switch 中的使用:
#include <stdio.h>
#include <stdlib.h>
enum color { red=1, green, blue };
int main()
{
enum color favorite_color;
/* 用户输入数字来选择颜色 */
printf("请输入你喜欢的颜色: (1. red, 2. green, 3. blue): ");
scanf("%u", &favorite_color);
/* 输出结果 */
switch (favorite_color)
{
case red:
printf("你喜欢的颜色是红色");
break;
case green:
printf("你喜欢的颜色是绿色");
break;
case blue:
printf("你喜欢的颜色是蓝色");
break;
default:
printf("你没有选择你喜欢的颜色");
}
return 0;
}
运行结果
6、typedef关键字
typedef的用途定义一种类型的别名
如:
#include <stdio.h>
typedef int zhenxin;
typedef char zifu;
int main(){
zhenxin i=20;
zifu str='c';
printf("%d %c\n",i,str);
return 0;
}
运行结果
typedef一般配合结构体用,方便嘛,不要每次都要struct开头
#include <stdio.h>
#include <string.h>
typedef struct{
int age;
char name[32];
}Mei;
void initMei(Mei p[],int len){
for(int i=0;i<len;i++){
printf("年龄:%d 姓名:%s \n",p[i].age,p[i].name);
}
}
void initMei2(Mei *p,int len){
for(int i=0;i<len;i++){
printf("年龄:%d 姓名:%s \n",p->age,p->name);
p++;
}
}
int main(){
Mei p[2];
p[0].age=21;
strcpy(p[0].name,"小美");
p[1].age=20;
strcpy(p[1].name,"大漂亮");
Mei *mei;
mei=p;
initMei(p,(sizeof(p)/sizeof(p[0])));
initMei2(mei,(sizeof(p)/sizeof(p[0])));
initMei2(p,(sizeof(p)/sizeof(p[0])));
return 0;
}
运行结果