文章目录
组合数据类型
一、结构体
C语言中提供了众多的基本数据类型,但是现实生活中的对象一般都不是单纯的整型,浮点型或者字符串,而是这些基本数据类型的综合体。比如一个学生,典型地应该拥有学号(整型),姓名(字符串),分数(浮点型),年龄(整型),性别(字符串)等不同的侧面属性,这些所有的属性都不应该被拆分开来,而是应该组成一个完整的整体,代表一个完整的学生。
C语言中,可以使用结构体来将多种不同的数据组装起来,形成某种现实意义的自定义数据类型,结构体本质上是一种自定义类型
1、基本语法
结构体的定义
struct 结构体标签
{
成员 1;
成员 2;
};
语法:
结构体标签:用来区分各个不同的结构体
成员:是包含在结构体内部的数据,可以是任意的数据类型
比如:定义一个结构体模板(声明一个结构体类型)
struct Studetn
{
int id;
char name[20];
char gender[10];
int age;
float score;
};
这里相当于定义了一个自定义的数据类型(struct Student)
2.结构体定义一个变量
///1 用结构体模板定义结构体变量
struct Student s1;
struct Student s2,s3;
///定义结构体模板时,直接定义变量
struct Student
{
int id;
char name[20];
char gender[10];
int age;
float score;
}s5,s6;
///使用匿名结构体模板定义变量
struct
{
int id;
char name[20];
char gender[10];
int age;
float score;
}s7;
注意:这样定义后,这个结构体就不能在定义其他变量了,因为结构体没有名字
3.结构体变量的初始化和赋值:
结构体跟普通变量一样,涉及到定义,初始化,赋值,取址,传参等等操作,这些操作绝大部分都跟普通变量别无二致,只有少数操作有些特殊性,这其实也是结构体这种组合类型的设计初衷,就是让开发者用起来比较顺手,不跟普通变量产生太多差异
结构体的定义和初始化:
由于结构体内部有多个不同类型的成员,因此初始化采用与数组类似的列表方式
结构体初始化有两种方式,一种普通初始化,一种是指定成员初始化
为了能够适应结构体的升级,一般建议采用指定成员初始化
//定义结构体变量的时候,一一初始化
struct Student s4 = {1001, "张三", "女", 88, 92.5};
//定义的时候,只初始化一部分
struct Student s8 = {1002, "李四", "女"};
//定义的时候,指定结构体成员进行初始化
struct Student s9 = {.id = 1003, .name = "王五", .gender = "女", .score = 92.5, .age = 20};
//使用一个已经初始化的结构体变量给另一个结构体变量赋初值
struct Student s10 = s9;
指定成员初始化的好处:
成员初始化的次序可以改变
可以初始化一部分成员
结构体新增成员之后,初始化语句仍然可用
结构体变量成员的访问
结构体相当于一个集合,内部包含了众多成员,每个成员都是独立的变量,都可以被独立的引用(访问),引用结构体成员非常简单,只需要一个成员引用符.即可
结构体变量.成员1
结构体变量.成员2
struct Student s1; //s1就是一个结构体变量
s1.id = 1004;
strcpy(s1.name, "Tom");
strcpy(s1.gender, "女");
s1.age = 20;
s1.score = 89.5;
struct Student s9 = {.id = 1003, .name = "Jack", .gender = "女", .score = 92.5, .age = 20};
printf("学号:%d\n", s9.id);
printf("姓名:%s\n", s9.name);
printf("性别:%s\n", s9.gender);
printf("年龄:%d\n", s9.age);
printf("成绩:%.2f\n", s9.score);
结构体包含特殊成员
结构体成员包含数组
#include <stdio.h>
#include <string.h>
struct Student2
{
int id;
char name[20];
char gender[10];
int age;
float score[3];
};
int main(int argc, char const *argv[])
{
struct Student2 s10 = {1005, "Puppy", "Male", 28, {92.5, 93}};
printf("学号:%d\n", s10.id);
printf("姓名:%s\n", s10.name);
printf("性别:%s\n", s10.gender);
printf("年龄:%d\n", s10.age);
printf("成绩:%.2f %.2f %.2f\n", s10.score[0], *(s10.score+1), s10.score[2]);
return 0;
}
结构体包含结构体变量
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Node
{
int age;
float score[3];
};
struct Student3
{
int id;
char name[20];
char gender[10];
struct Node n;
};
int main(int argc, char const *argv[])
{
char na[20] = "Rose";
struct Student3 s11 = {1006, "Jenny", "female", {28, {92, 93, 94}}};
//strcpy(s11.name, "Rose");
strcpy(s11.name, na);
printf("学号:%d\n", s11.id);
printf("姓名:%s\n", s11.name);
printf("性别:%s\n", s11.gender);
printf("年龄:%d\n", s11.n.age);
printf("成绩:%.2f %.2f %.2f\n", s11.n.score[0], *(s11.n.score+1), s11.n.score[2]);
return 0;
}
结构体数组
每个元素都是一个结构体的变量
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Student
{
int id;
char name[20];
float score;
};
int main(int argc, char const *argv[])
{
struct Student s1;//结构体变量
struct Student s2[5] = {
{1001, "张三", 92.5},
{1002, "李四", 93.5},
{1003, "王五", 94.5},
{1004, "赵六", 95.5},
{1005, "田七", 96.5}
};
struct Student s3[5];
for (int i = 0; i < 5; ++i)
{
scanf("%d%s%f", &s3[i].id, s3[i].name, &s3[i].score);
}
for (int i = 0; i < 5; ++i)
{
printf("%d\t%s\t%.2f\n", s2[i].id, s2[i].name, s2[i].score);
}
return 0;
}
结构体指针
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Student
{
int id;
char name[20];
float score;
};
int main(int argc, char const *argv[])
{
// struct Student *ps1; //结构体指针,这里是一个野指针
// ps1->id = 1003;//错误,ps1没有分配合法的空间
// struct Student *ps4 = {1004, "李四", 90};//不可以
//结构体指针变量的初始化
struct Student s1;
struct Student *ps2 = &s1;
//结构体指针通过malloc匿名申请
struct Student *ps3 = malloc(sizeof(struct Student));
//访问结构体指针变量的成员
ps3->id = 1003;
strcpy(ps3->name, "张三");
ps3->score = 92.5;
printf("ps3->id = %d\n", ps3->id);
printf("ps3->name = %s\n", ps3->name);
printf("ps3->score = %.2f\n", ps3->score);
return 0;
}
dent *ps3 = malloc(sizeof(struct Student));
//访问结构体指针变量的成员
ps3->id = 1003;
strcpy(ps3->name, "张三");
ps3->score = 92.5;
printf("ps3->id = %d\n", ps3->id);
printf("ps3->name = %s\n", ps3->name);
printf("ps3->score = %.2f\n", ps3->score);
return 0;
}
普通结构体变量传参
void init(struct Student *s1)
{
scanf("%d%s", &s1->id, s1->name);
}
void display(struct Student s1)
{
printf("%d\t%s\n", s1.id, s1.name);
}
int main(int argc, char const *argv[])
{
struct Student s1;
init(&s1);
display(s1);
return 0;
}
结构体指针变量传参
#include <stdio.h>
#include <stdlib.h>
struct Student
{
int id;
char name[20];
};
void init(struct Student *s2)
{
// scanf("%d%s", &s2->id, s2->name);
scanf("%d%s",&s2[0].id,s2[0].name);//可以看作数组首元素
}
void display(struct Student *s2)
{
// printf("%d\t%s\n", s2->id, s2->name);
printf("%d\t%s\n", s2[0].id, s2[0].name);
}
int main(int argc, char const *argv[])
{
struct Student *s2 = malloc(sizeof(struct Student));
init(s2);
display(s2);
return 0;
}
二、联合体
联合体是C语言中另外一种可以包含多个类型不同成员的复杂类型,联合体同一时刻只保留一个成员的值,如果重新对另外的成员赋值,那么会将原来的成员的值覆盖
联合体定义:
union test
{
int a;
char b;
double c;
};
联合体内部成员的这种特殊的“堆叠”效果,使得联合体有如下特征:
整个联合体的尺寸,取决于联合体中尺寸最大的值
给联合体的某个成员赋值,会覆盖其他的成员,使他们失效
联合体成员之间行程一种“互斥”的逻辑,在某个时刻只有一个成员有效
结构体和联合体的区别:
结构体每个成员是分配独立空间,互相不影响,联合体所有的成员是共用同一块空间,修改一个成员会影响其他成员,结构体占用的内存大于等于所有成员占用内存的和,联合体占用内存等于最大成员占用的内存大小
联合体的应用:
联合体的操作跟结构体形式上别无二致,但由于联合体特殊的存储特性,不管怎么初始化和赋值,最终都有且仅有一个成员是有效的。
联合体一般在编程中很少使用,在单片机用的相对较多一点,而且经常和结构体一起用作为结构体的一个成员,将一些互斥的属性,用联合体表示,节省空间的同时,让互斥属性特征更加明显
大端和小端:
大端模式:
是指数据的高字节,保存在内存的低地址,而数据的低字节放在内存的高地址中,这种存储模式就叫大端序
小端模式:
是指数据的高字节,保存在内存的高地址,而数据的低字节放在内存的低地址中,这种存储模式就叫小端序
简单来说,小端模式就是数据在存放的时候,存放在低地址,大端模式就是数据在存放的时候,存放在高地址
#include <stdio.h>
union Us
{
int a;
char c;
}
int main(void)
{
union Us u;
u.a=1;
if(u.c==1)
printf("小端");
else
printf("大端");
//方式二:
int b = 0x12345678;
char c = *((char*)(&b));
if(c == 0x78) //小端 高地址数据存放在低地址
printf("小端");
else if(c == 0x12)
printf("大端");
return 0;
}
三、枚举
实际编程中,有些数据的取值往往有一定范围,只能是少量的整数,这个时候我们可以给这些整型值取一个名字,方便代码的阅读和维护(就是给整型常量取名字)
enum Week{Mon, Tus, Wed, Thu, Fri, Sat, Sun};
枚举常量实际上就是整型,首个枚举常量默认为0
枚举常量在定义时可以赋值,若不赋值,则取前面的枚举常量的值加1
C语言中,枚举等价与整型,支持整型数据的一切操作
枚举数据最重要的作用,是使用有意义的单词,来代替无意义的数字,提高程序的可读性
Mon, Tus, Wed, Thu, Fri, Sat, Sun这些放在?
这些都不是变量,他们不占用栈内存,堆内存,数据段,在代码段中
因为我们的枚举是直接编译到命令里面的,放在代码段中,所以不能&,这也是枚举的本质
注意:
一般我们将结构体类型名,联合体类型名,枚举名和枚举量用大写字母开头
普通变量名我们一般用小写字母
宏名一般全部用大写字母
test:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#define MAXNAME 30
int n=6; //商品数量
struct Goods
{
int num; //序号
char name[MAXNAME]; //名字
int price; //价格
int left_num; //剩余数量
};
void menu()
{
printf("------shopping mall------\n");
printf("-------显示商品(1)-------\n");
printf("-------添加商品(2)-------\n");
printf("-------删除商品(3)-------\n");
printf("-----更改商品价格(4)-----\n");
printf("-------查找商品(5)-------\n");
printf("-------购买商品(6)-------\n");
printf("--------退出(0)----------\n");
}
void init(struct Goods *p)
{
// printf("序号\t名字\t价格\t数量\t\n");
for(int i=0;i<n;i++)
{
p[i].num=i+1;
}
strcpy(p[0].name,"西瓜");
p[0].price=22;
p[0].left_num=3;
strcpy(p[1].name,"苹果");
p[1].price=21;
p[1].left_num=4;
strcpy(p[2].name,"梨子");
p[2].price=23;
p[2].left_num=6;
strcpy(p[3].name,"芒果");
p[3].price=23;
p[3].left_num=10;
strcpy(p[4].name,"香蕉");
p[4].price=21;
p[4].left_num=7;
strcpy(p[5].name,"葡萄");
p[5].price=22;
p[5].left_num=8;
}
void showAll(struct Goods *p)
{
printf("序号\t名字\t价格\t数量\t\n");
for(int i=0;i<n;i++)
printf("%d\t%s\t%d\t%d\n",p[i].num,p[i].name,p[i].price,p[i].left_num);
}
struct Goods * insert(struct Goods *p)
{
p=(struct Goods *)realloc(p,(++n)*sizeof(struct Goods));
printf("输入商品信息\n");
printf("序号\t名字\t价格\t数量\t\n");
scanf("%d%s%d%d",&p[n-1].num,p[n-1].name,&p[n-1].price,&p[n-1].left_num);
printf("添加成功!\n");
return p;
}
struct Goods * delLineInfo(struct Goods *p,int m)
{
for(int i=m;i<n-1;i++)
{
p[i]=p[i+1];
}
p=(struct Goods *)realloc(p,(--n)*sizeof(struct Goods));
return p;
}
int getNum()
{
int num;
printf("输入序号:");
scanf("%d",&num);
return num;
}
char *getName()
{
printf("输入名字:");
char *name=malloc(sizeof(char)*MAXNAME);
scanf("%s",name);
return name;
}
struct Goods * delGoodsWays(struct Goods *p,int op)
{
int flag=1;
char *name=NULL;
int num= INT_MIN;
if(op==1)
{
num = getNum();
}
else if(op==2)
{
name =getName();
}
for(int i=0;i<n;i++)
{
if((op==1&&p[i].num==num)||(op==2&&strcmp(p[i].name,name)==0))
{
p = delLineInfo(p,i);
printf("删除成功!\n");
flag=0;
break;
}
}
if(flag)
printf("未找到该物品!\n");
return p;
}
int chooseOp()
{
int op;
printf("选择方式(1:序号 2:名字): ");
scanf("%d",&op);
return op;
}
struct Goods * chooseDelLineInfo(struct Goods *p)
{
int op=chooseOp();
switch(op)
{
case 1:
p = delGoodsWays(p,op);
break;
case 2:
p = delGoodsWays(p,op);
break;
default:
printf("输入有误请重新输入!\n");
op=chooseOp();
}
return p;
}
struct Goods * chgGoodsWays(struct Goods *p,int op)
{
int flag=0;
char *name=NULL;
int num= INT_MIN;
if(op==1)
{
num = getNum();
}
else if(op==2)
{
name =getName();
}
for(int i=0;i<n;i++)
{
if((op==1&&p[i].num==num)||(op==2&&strcmp(p[i].name,name)==0))
{
flag=0;
printf("请输入修改价格:");
scanf("%d",&p[i].price);
printf("修改成功!\n");
break;
}
else
{
flag=1;
}
}
if(flag)
printf("未找到需要修改的物品!\n");
return p;
}
struct Goods * changeLineInfo(struct Goods *p)
{
int op=chooseOp();
switch(op)
{
case 1:
p = chgGoodsWays(p,op);
break;
case 2:
p = chgGoodsWays(p,op);
break;
default:
printf("输入有误请重新输入!\n");
op=chooseOp();
}
return p;
}
void searchGoodsWays(struct Goods *p,int op)
{
int flag=0;
char *name=NULL;
int num= INT_MIN;
if(op==1)
{
num = getNum();
}
else if(op==2)
{
name =getName();
}
for(int i=0;i<n;i++)
{
if((op==1&&p[i].num==num)||(op==2&&strcmp(p[i].name,name)==0))
{
flag=0;
printf("\n序号\t名字\t价格\t数量\t\n");
printf("%d\t%s\t%d\t%d\n",p[i].num,p[i].name,p[i].price,p[i].left_num);
break;
}
else
{
flag=1;
}
}
if(flag)
printf("未找到该物品!\n");
}
void searchLineInfo(struct Goods *p)
{
int op=chooseOp();
switch(op)
{
case 1:
searchGoodsWays(p,op);
break;
case 2:
searchGoodsWays(p,op);
break;
default:
printf("输入有误请重新输入!\n");
op=chooseOp();
}
}
struct Goods * buyGoodsWays(struct Goods *p,int op)
{
int flag=0;
int flag2=1;
char *name=NULL;
int num= INT_MIN;
if(op==1)
{
num = getNum();
}
else if(op==2)
{
name =getName();
}
for(int i=0;i<n;i++)
{
if((op==1&&p[i].num==num)||(op==2&&strcmp(p[i].name,name)==0))
{
flag=0;
int buyNum=0;
int price=0;
printf("请输入购买的数量: ");
scanf("%d",&buyNum);
if(buyNum>p[i].price)
{
printf("剩余的商品数量不足\n");
break;
}
else
{
int ackNum=0;
price = p[i].price*buyNum;
printf("需要支付的金额:%d\n",price);
printf("是否确认支付(1:确认支付,2:返回): ");
scanf("%d",&ackNum);
if(ackNum==1)
{
p[i].price -= buyNum;
printf("购买成功!\n");
flag2=0;
}
else
break;
}
break;
}
else
{
flag=1;
}
}
if(flag&&flag2)
printf("未找到需要修改的物品!\n");
return p;
}
struct Goods * buyGoods(struct Goods *p)
{
int op=chooseOp();
switch(op)
{
case 1:
p=buyGoodsWays(p,op);
break;
case 2:
p=buyGoodsWays(p,op);
break;
default:
printf("输入有误请重新输入!\n");
op=chooseOp();
}
return p;
}
int main(int argc, char const *argv[])
{
int op=0;
struct Goods *p=(struct Goods *)malloc(n*sizeof(struct Goods));
init(p);
menu();
while(1)
{
// menu();
printf("\n\n请操作(0-6)\n");
scanf("%d",&op);
switch(op)
{
case 1:
system("clear");
menu();
showAll(p);
break;
case 2:
system("clear");
menu();
p=insert(p);
break;
case 3:
system("clear");
// menu();
showAll(p);
p=chooseDelLineInfo(p);
break;
case 4:
system("clear");
menu();
p=changeLineInfo(p);
break;
case 5:
system("clear");
menu();
searchLineInfo(p);
break;
case 6:
system("clear");
// menu();
showAll(p);
p=buyGoods(p);
break;
case 0:
exit(0);
default:
break;
}
}
free(p);
return 0;
}