【C语言】结构体、位段、枚举、联合(共用体)

结构体

结构:一些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量;

结构体声明:struct是结构体关键字,结构体声明不能省略struct;

匿名结构体:只能在声明结构体的时候声明结构体成员,之后再想用这个结构体声明结构体成员时就不行了;因为匿名结构体只能用一次,没有结构体名字没有办法声明结构体成员;(只打算用一次的结构体)

【C语言】结构体、位段、枚举、联合(共用体)_第1张图片


 结构体变量的声明与初始化

结构体变量的初始化需要使用大括号进行初始化;

// 结构体成员的声明与初始化
struct student 
{ 
	// 声明的是一个描写学生的结构体
	char name[20];
	int age;
	char sex[5];
} s1 = { "张三", 18, "男" }; // 声明的是结构体成员并初始化;
int main()
{
	struct student s2 = { "李四", 19, "女" }; // 声明的是结构体成员并初始化;
	struct student s3 = { .age = 20, .name = "王五", .sex = "男" }; // 指定初始化结构体成员变量,通过 . 指定;
	// 如果不指定初始化结构体成员变量,就顺序初始化;
	printf("%s %d %s\n", s1.name, s1.age, s1.sex);
	printf("%s %d %s\n", s2.name, s2.age, s2.sex);
	printf("%s %d %s\n", s3.name, s3.age, s3.sex);
	return 0;
}

 结构的自引用

可以包含同类型的结构体指针,但是不能包含同类型结构体;

#include 
struct Node
{
	int data; // 存储数据 -> 数据域
	struct Node* n; // 存放下一个节点的地址 -> 指针域
	// 可以包含同类型结构体指针,但是不能包含同类型结构体
};

结构体内存对齐

怎么对齐:

1、第一个成员在结构体偏移量为0的地址处存放;

2、其他成员变量要对齐到对齐数的整数倍的地址处;

3、对齐数:VS默认对齐数、结构体成员自身大小就是对齐数,取较小的对齐数

4、结构体总大小为最大对齐数的整数倍(每个成员都有一个对齐数),所有成员中的最大对齐数

5、嵌套结构体的对齐,嵌套的结构体要对齐到自身最大对齐数的整数位;结构体的大小就是所有成员中的最大对齐数的整数倍

为什么对齐:

1、不是所有硬件平台都能访问任意地址上的数据的,有些只能在特定位置处取某些特定数据;

2、结构体(尤其是栈)应该尽可能在自然边界上对齐;未对齐的内存,处理器需要访问两次;而对齐的只需访问一次;

3、结构体的内存对齐就是拿空间换取时间的;

如何节省空间:让占用空间小的成员集中在一起;

【C语言】结构体、位段、枚举、联合(共用体)_第2张图片

代码验证: 
#include 
int main()
{
	struct S
	{
		char c1;
		int a;
		double b;
		char c2;
	}s;
	struct S1
	{
		int a;
		char c1;
		char c2;
		double b;
	}s1;
	printf("%d\n", sizeof(s));
	printf("%d\n", sizeof(s1));
	return 0;
}

百度笔试题: 

 写一个宏,计算结构体中某变量相对于首地址的偏移(offsetof宏);

 offsetof宏的使用:
#include 
#include 
int main()
{
	struct S
	{
		char c1;
		int a;
		double b;
		char c2;
	}s;
	struct S1
	{
		int a;
		char c1;
		char c2;
		double b;
	}s1;
	// offsetof宏:结构体成员变量相对于首地址的偏移量
	printf("%d %d %d %d\n", offsetof(struct S, c1), offsetof(struct S, a), offsetof(struct S, b), offsetof(struct S, c2));
	printf("%d %d %d %d\n", offsetof(struct S1, a), offsetof(struct S1, c1), offsetof(struct S1, c2), offsetof(struct S1, b));
	printf("%d\n", sizeof(s));
	printf("%d\n", sizeof(s1));
	return 0;
}

结构体传参:

1、传值调用,形参相当于一份临时拷贝,修改形参不会改变实参;

2、传址调用,如果不加上const,修改新参就会改变实参;

3、传址调用比传值调用更节省空间,结构体传参尽量传址;


位段

1、设计位段的目的就是为了节省空间;

2、位段的成员必须是int、unsigned int、signed int(c99之后,其他类型也可以,但是基本都是int、char类型);

3、位段的成员名后边有一个冒号和一个数字(数字表示几个比特位);

4、位段:为表示二进制位;

#include 
int main()
{
	struct S1 // 结构体
	{
		int a;
		int b;
		char c;
	};
	struct S2 // 位段
	{
		int a : 2;
		int b : 4;
		char c : 1;
	};
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

 位段的内存分配

1、不知道是从低位开始分配还是从高位才是分配,这是不确定的;

【C语言】结构体、位段、枚举、联合(共用体)_第3张图片


 位段的跨平台问题:

1、int位段被当成有符号数还是无符号数是不确定的;

2、位段中最大位的值是不确定的;

3、位段中的成员在内存中,从右到左还是从左到右分配标准未定义;

4、一个结构包含两个位段,第二个位段无法容纳于第一个位段剩余的位时,是舍弃位还是利用,是不确定的;


枚举

枚举类型的定义:

1、把可能的取值一一列举(月份、星期);

2、必须包含关键字enum;

3、枚举的值是默认从0一次递增的;

4、枚举常量的默认值是可以修改的;

#include 
enum Color
{
	RED, // 注意,这是逗号,不是分号
	GREEN,
	BLUE  // 最后这里什么符号都没有
};
enum Sex
{
	MALE = 2, // 枚举常量的默认值是可以修改的
	FEMALE = 4,
	CECRECY = 1
};
int main()
{
	printf("%d\n", RED);
	printf("%d\n", GREEN);
	printf("%d\n", BLUE); // 打印枚举常量
	printf("%d\n", FEMALE);
	return 0;
}

 枚举的优点:

1、枚举可以增加代码的可读性和可维护性;

2、#define是用于替换的,没有类型的,但枚举有类型检查;

3、可以一次定义多个常量;


联合(共用体)

1、联合定义的变量包含一系列的成员;

2、这些成员共用同一块空间,所以也叫做共用体;

3、在同一时间段内,联合体内的成员不能同时使用,只能使用其中一个;

4、如果修改联合体内其中一个成员的值,则另一个的值也会改变;

5、联合体的关键字:union;

#include 
union Un
{
	int a;
	char b;
};
int main()
{
	union Un un;
	un.a = 10;
	un.b = 'a'; 
	printf("%d\n", sizeof(un)); // 大小是成员中较大的那个大小
	printf("%p\n", &un);
	printf("%p\n", &(un.a));
	printf("%p\n", &(un.b)); // 说明联合体共用一块空间
	return 0;
}

 联合体大小的计算

1、联合体的大小是最大成员的大小,这句话是错的;

2、联合体的大小至少是最大成员的大小;

3、当最大成员大小没有对齐到最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍处;

#include 
union Un
{
	short a[7]; // 对齐数 2
	int b; // 对齐数 4
	// a占14个字节
	// 但是不是最大对齐数的整数倍
	// 所以联合体的大小是16
}un;
int main()
{
	printf("%d\n", sizeof(un));
	return 0;
}

你可能感兴趣的:(c语言,数据结构,开发语言)