结构体和枚举

一、结构体

1.1结构体变量的定义和初始化

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct Point
{
    
    
	int x;
	int y;
};
struct Node
{
    
    
	int data;
	struct Point P;
	struct Node* next;//结构体正确的自引用方式
}n1 = {
    
     10, {
    
    4, 5}, NULL };//结构体嵌套初始化
struct Node n2 = {
    
     20, {
    
    5, 6}, NULL };

1.2结构体内存对齐(计算结构体的大小)

1.2.1结构体内存对齐规则

1、结构体的第一个成员放在0偏移处。
2、从第二个成员开始,以后的每个成员都要对齐到某个对齐数的整数倍处。这个对齐数是:成员自身的大小和默认对齐数的较小值。
备注:vs环境下,默认对齐数是8。
gcc环境下,没有默认对齐数,没有默认对齐数时,对齐数就是成员自身大小。
3、当成员全部存放进去后,结构体的总大小必须是所有成员的对齐数中最大对齐数的整数倍。如果不够,则浪费空间对齐。
4、如果嵌套了结构体,嵌套的结构体成员要对齐到自己的成员的最大对齐数的整数倍处。最后总大小必须是最大对齐数的整数倍,最大对齐数包含嵌套的结构体成员自己的成员中的对齐数。

struct s3
{
    
    
	double d;
	char c;
	int i;
};
struct s4
{
    
    
	char c1;
	struct s3 s3;
	double d;
};
int main()
{
    
    
	printf("%d\n", sizeof(struct s3));
	printf("%d\n", sizeof(struct s4));
	return 0;
}

在这里插入图片描述
在这里插入图片描述

1.2.2存在结构体内存对齐的原因

1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而访问对齐的内存时仅需要作一次访问。
总体来说,结构体的内存对齐是用空间来换取时间的做法。为了既满足对齐,又节省空间,我们在设计结构体的时候,应让占用空间小的成员尽量集中在一起。例如:

struct s1
{
    
    
	char c1;
	int i;
	char c2;
};
struct s2
{
    
    
	char c1;
	char c2;
	int i;
};

s1和s2类型的成员一模一样,但s2比s1所占的空间更小。

1.2.3修改默认对齐数

结构体内存在对齐方式不合适的时候,我们可以自己修改默认对齐数。

#pragma pack(8)//设置默认对齐数为8
struct s1
{
    
    
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认对齐数

1.3结构体传参

结构体传参的时候,应传结构体的地址。

struct s1
{
    
    
	char c1;
	int i;
	char c2;
};
struct s1 s = {
    
     'A', 100, 'a' };
void print1(struct s1 s)//传结构体传参
{
    
    
	printf("%d\n", s.c1);
}
void print2(struct s1* ps)//传结构体地址传参
{
    
    
	printf("%d\n", ps->c1);
}
int main()
{
    
    
	print1(s);
	print2(&s);
	return 0;
}

应选用print2函数,因为函数传参的时候,参数需要压栈,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销过大,会导致性能下降。

二、结构体实现位段

2.1位段的内存分配

1、位段的成员是int,unsigned int,signed int或者char(属于整型家族)类型。
2、位段在空间上是按照需要以4个字节(int)或1个字节(char)的方式来开辟的(使用的)。
3、位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

struct S
{
    
    
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
int main()
{
    
    
	struct S s = {
    
     0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	printf("%d\n", sizeof(struct S));
	return 0;
}

在这里插入图片描述
在这里插入图片描述

扫描二维码关注公众号,回复: 15851860 查看本文章

2.2位段不跨平台的原因

1、位段的int成员被当成有符号数还是无符号数是不确定的。
2、位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器上会出现问题。)
3、位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4、当一个结构包含两个位段成员,第二个位段成员比较大,无法容纳于第一个位段成员剩余的位时,是舍弃剩余的位还是利用剩余的位,这是不确定的。
总结:位段可以很好地节省空间,但有跨平台的问题存在。

三、枚举

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
enum Sex
{
    
    
	MALE,//枚举类型的可能取值,也叫枚举常量
	FEMALE = 5,//这些可能取值是有值的,默认从0开始,一次递增1
	SECRET//在定义时也可以赋初值
};
int main()
{
    
    
	enum Sex s = FEMALE;//只能拿枚举常量给枚举变量赋值
	printf("%d \n", MALE);
	printf("%d \n", FEMALE);
	printf("%d \n", SECRET);
	return 0;
}

在这里插入图片描述
枚举的优点:
1、增加代码的可读性和可维护性。如写游戏时枚举常量EXIT本质是0,看到的是退出;枚举常量PLAY本质是1,看到的是玩游戏。
2、和#define定义的常量相比枚举有类型检查,更加严谨。
3、和#define定义的常量相比便于调试。
4、和#define定义的常量相比使用方便,一次可以定义多个常量。

猜你喜欢

转载自blog.csdn.net/zhanlongsiqu/article/details/129651178