组合数据类型

组合数据类型

一、结构体

​ 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;
};

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MSqjDbY4-1650630107178)(C:\Users\yyh\AppData\Roaming\Typora\typora-user-images\image-20220422200817242.png)]

联合体内部成员的这种特殊的“堆叠”效果,使得联合体有如下特征:

整个联合体的尺寸,取决于联合体中尺寸最大的值

给联合体的某个成员赋值,会覆盖其他的成员,使他们失效

联合体成员之间行程一种“互斥”的逻辑,在某个时刻只有一个成员有效

结构体和联合体的区别:

​ 结构体每个成员是分配独立空间,互相不影响,联合体所有的成员是共用同一块空间,修改一个成员会影响其他成员,结构体占用的内存大于等于所有成员占用内存的和,联合体占用内存等于最大成员占用的内存大小

联合体的应用:

​ 联合体的操作跟结构体形式上别无二致,但由于联合体特殊的存储特性,不管怎么初始化和赋值,最终都有且仅有一个成员是有效的。

​ 联合体一般在编程中很少使用,在单片机用的相对较多一点,而且经常和结构体一起用作为结构体的一个成员,将一些互斥的属性,用联合体表示,节省空间的同时,让互斥属性特征更加明显

大端和小端:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y3Li3eqx-1650630107179)(C:\Users\yyh\AppData\Roaming\Typora\typora-user-images\image-20220422201107925.png)]

大端模式:

​ 是指数据的高字节,保存在内存的低地址,而数据的低字节放在内存的高地址中,这种存储模式就叫大端序

小端模式:

​ 是指数据的高字节,保存在内存的高地址,而数据的低字节放在内存的低地址中,这种存储模式就叫小端序

简单来说,小端模式就是数据在存放的时候,存放在低地址,大端模式就是数据在存放的时候,存放在高地址

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yengi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值