【c语言】自定义类型之结构体-联合体-枚举

一.结构体

结构体是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
结构体框架如下

1.认识结构体

struct
{

};

2.定义一个结构体

struct Stu
	//Stu为定义的该结构体的名称
{
	char name[20];
	int age;
	float score;
	//{}内 为该结构体的成员 
};
//注意末尾的分号

3.定义结构体变量

struct Point
{
int x;
int y;
};
struct Stu
{
	char name[20];
	int age;
	float score;
}A;
//此时A为全局变量
int main()
{
	struct Stu B;
	//此时B为局部变量
	struct Stu A = { "wangwu",20,63.4 };
	//可以按照顺序给结构体变量进行赋值
	//当然也可以不按照顺序 如下】
	struct Stu B = { .age = 30,.name = "lisi",.score = 66.7 };

	return 0;
}

4.结构体成员访问操作符 . ->

结构体变量.成员变量名
结构体指针—>成员变量名
.

struct Stu
{
	char name[20];
	int age;
	float score;
}A;
int main()
{
	struct Stu A = { "zhangsan",20,67.32 };
	printf("%f %d %s", A.score, A.age, A.name);
	return 0;

}

->

#include <string.h>
struct Stu
{
	char name[20];
	int age;
	float score;
}A;
void print_stu(struct Stu s)
{
	printf("%s %d %f\n", s.name, s.age,s.score);
}
set_stu(struct Stu* ps)
{
	ps->age = 88;
	strcpy(ps->name , "guoleile666");
	ps->score = 98.5;
}
int main()
{
	struct Stu A = { "zhangsan",20,30.5 };
	print_stu(A);
	set_stu(&A);
	print_stu(A);
	return 0;
}

在这里插入图片描述

5.结构体内存对齐

声明定义好结构体后 就需要求他的大小 这里就用到内存对齐的知识

struct s1
{
	char s1;
	char s2;
	int a;
};
struct s2
{
	char s1;
	int a;
	char s2;
};
//s1和s2只是顺序不一样
int main()
{
	struct s1 s3 = { "sss","lll",666 };
	struct s2 s4 = { "sss",666,"lll" };
	printf("%zd\n", sizeof(s3));
	printf("%zd\n", sizeof(s4));
	return 0;
}

在这里插入图片描述
内存对齐规则

  1. 结构体的第⼀个成员对⻬到相对结构体变量起始位置偏移量为0的地址处
  2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
    对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
  • VS中默认的值为8
  • Linux中没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
  1. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的
    整数倍。
  2. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构
    体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
struct S1
{
	char c1;  1   8   1
 //第一个1是char类型所占字节 8为默认对齐数 第二个1为 最终取得的对齐数
	int i;    4  8   4
	char c2;  1  8   1
};

int main()
{
	printf("%d\n", sizeof(struct S1));
	//结果为12
	return 0;
}

在这里插入图片描述

struct S2
{
	char c1;
	char c2;
	int i;
};

int main()
{
	printf("%d\n", sizeof(struct S2));
	//结果为8
	return 0;
}

在这里插入图片描述
结构体嵌套求所占内存

struct S3
{
	double d;
	char c;
	int i;
};
int main()
{
	printf("%d\n", sizeof(struct S3));
	//结果为16
	return 0;
}

在这里插入图片描述

struct S3
{
	double d;
	char c;
	int i;
};
struct S4
{
	char c1;     1  8   1
	struct S3 s3;  16 
	double d;      8    8    8
};
int main()
{
	printf("%d\n", sizeof(struct S4));
	//结果为32 根据第四条规则 取S3中最大的8 从8的倍数的位置开始对齐 
	//s3的大小是16 对齐16位
	return 0;
}

在这里插入图片描述

修改默认对其数

#pragma pack(1//修改默认为1

#pragma pack() /括号里什么都不写 取消设置的默认对齐数

位段

  1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以
    选择其他类型。
  2. 位段的成员名后边有⼀个冒号和⼀个数字。
    数字代表二进制位
struct A
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};
struct B
{
	int a ;
	int b ;
	int c ;
	int d ;
};
int main()
{
	printf("%zd", sizeof(struct A));
	printf("%zd", sizeof(struct B));
	//A结果为8 
	//B结果为16
}

在这里插入图片描述
这里的分析有些问题 仔细观察位段规则

  1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
    这里首先会开辟4个字节的空间,a占2bit、b占5bit、c占10bit,还剩15bit的空间。但d却需要30bit。这时4个字节的空间不够用了,那就再开辟4个字节的空间

位段注意事项

不能对位段的成员使用&操作符 也不能使用scanf
因为可能两个成员共用一个地址哦

二.联合体

和结构体一样,联合体也是由一个或多个成员构成
但是联合体多个成员共用一个地址
联合体给一个成员赋值,其他成员的值也跟着变化

struct s
{
	char a;
	int b;
};
union a
{
	char a;
	int b;
};
int main()
{
	printf("%zd\n", sizeof(struct s));  //8
	printf("%zd\n", sizeof(union a));   //4
}

联合体的大小
1.联合的大小至少是最⼤成员的大小。
2.当最大成员大小不是最大对齐数的时候,就要对齐到最大对齐数的整数倍
例如

union a
{
	char a[5]; //按照成员大小   1   8   1
	int b;     //              4   8   4
	//这里5 不满足于4的倍数 所以5--> 8 来满足需要
};
int main()
{
	printf("%zd\n", sizeof(union a));
    //结果为8
}

联合体判断大小端

union s
{
	int i;
	char c;

};
int main()
{
	union s U;
	//创建一个union s 类型的变量U 
	//s是这个联合体的名字
	U.i = 1;
	if (U.c == 1)
		printf("小端");
	else
		printf("大端");
	return 0;
}

优化联合体判断大小端

int judge_sys()
{
	union 
	{
		char a;
		int i;

	}U;
	U.i = 1;
	return	U.a;
}
int main()
{
	int ret = judge_sys;
	if (ret == 1)
		printf("小端");
	else
		printf("大端");
	return 0;
}

三.枚举

enum Day
{
	//列出的是枚举的可能取值
	//这些列出的可能取值被称为:枚举常量

	Mon,//默认为0
	Tues,//1
	Wed,//2
	Thur,//3
	Fri,//4
	Sat,//5
	Sun//6

};
int main()
{
	printf("%d %d %d %d %d %d %d", Mon, Tues, Wed, Thur, Fri, Sat, Sun);
   //当要修改默认值时 Mon=5;   这样时不可以的 因为Mon时常量 默认为1 
	//需要修改时 可以在枚举定义里修改 
	// emun Day
	//{
    //      Mon =1;
	//    这样后面也会改变 变成 2 3 4 5 6 7
	//    也可以改成不同的顺序 例如
	//     Mon=2;
	//     Tues=1;这样也是可以的
	//     也可以赋值为相同的值 但一般不这样做 
	//}
	return 0;
}

使用枚举常量给枚举变量赋值

int main()
{
	enum Day Friastday = Mon;
	return 0;
}

枚举的优点

1. 增加代码的可读性和可维护性

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void menu()
{
	printf("********************\n");
	printf("***    0.play   ****\n");
	printf("***1.add  2.sub ****\n");
	printf("***3.mul  4.div ****\n");
	printf("********************\n");
}
enum
{
	play,
	add,
	sub,
	mul,
	div
};
int main()
{
	int input = 0;
	scanf("%d", &input);
	while (input)
	{
		switch (input)
		{
		case add:
			//因为枚举已经声明add为1 sub为2 mul为3 div为4 
			//所以这里用add代替原先的 case 1:  case 2: ......
			//相较于原先 提升可读性  一进入switch循环 case 就可以清楚的知道进行哪一步
			break;
		case sub:
			break;
		case mul:
			break;
		case div:
			break;
		}
	}
	return 0;
}

2. 和#define定义的标识符⽐较枚举有类型检查,更加严谨。

在这里插入图片描述
而如果 #define add 1;
光标再放到add上时 就不会出现上述现象

3. 便于调试,预处理阶段会删除 #define 定义的符号

4. 使⽤⽅便,⼀次可以定义多个常量

这里枚举一下子定义四个 而define需要一个一个定义

5. 枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤

  • 16
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱吃南瓜的北瓜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值