结构体之内存对齐


前言

关于结构体(例如:内存对齐)、位段、枚举、联合等一些知识点的整理。


一、结构体

1.结构体内存对齐

(1)内存对齐规则

1、第一个成员在与结构体变量偏移量为0的地址处;
2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处(对齐数=编译器默认的一个对齐数 与 该成员大小的较小值)( vs中默认的值为8);
3、结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍;
4、如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构的对齐数)的整数倍。

(2)内存对齐计算
struct S1 {
char a;//(1,8)->1 //char的大小为1,vs默认对齐数为8,选择较小的1为对齐数
int b;//(4,8)->4//int的大小为4,vs默认对齐数为8,选择较小的4为对齐数
int c;//(4,8)->4//同上
}; 

如图所示:
在这里插入图片描述
嵌套了结构体的情况:

struct S2 {
int d;// (4,8)->4 
struct S1 s;//(1,4,4)->4//由于Struct S1中的最大对齐数是4,所以s的对齐数是4
};

如图所示:
在这里插入图片描述

(3)修改默认对齐数

1.vs中默认的值为8;
2.使用 【#pragma pack(4) //修改默认对齐数为4】修改默认对齐数。

代码如下(示例):

struct S3 {
	char a;//(1,8)->1
	int b;//(4,8)->4
	int c;//(4,8)->4
}; 
struct S4 {
	int d;// (4,8)->4
	struct S3 s3;//(1,4,4)->4
};
int main() {
	struct S3 s3;
	struct S4 s4;
	printf("%d\n", sizeof(s3));//12

	printf("%d\n", sizeof(s4));//16
	printf("***************\n");
	//offsetof - 宏
	//计算结构体成员相对起始位置的偏移量的

	printf("%u\n", offsetof(struct S3,a));//0
	printf("%u\n", offsetof(struct S3,b));//4
	printf("%u\n", offsetof(struct S3,c));//8
	printf("***************\n");
	printf("%u\n", offsetof(struct S4, d));//0
	printf("%u\n", offsetof(struct S4, s3));//4
	printf("***************\n");
	//修改默认对齐数
	//#pragma pack(4)//修改默认对齐数为4
	printf("%d\n", sizeof(s3));//9
	printf("%u\n", offsetof(struct S3, a));//0
	printf("%u\n", offsetof(struct S3, b));//1
	printf("%u\n", offsetof(struct S3, c));//5

	return 0;
}

2.结构体内存不对齐

1、平台原因(移植原因);
2、性能原因:如果结构体内存不对齐进行存储,可能导致同一变量多次访问。
在这里插入图片描述
结构体的内存对齐是拿空间来换取时间的做法

3.结构体传参

结构体传参的时候,要传结构体的地址。
代码如下(示例):

struct S {
	char name[20];
	int age;
}s2;
int main() {
	struct S s1;
	scanf("%s %d", &s1.name, &s1.age);
	scanf("%s %d", &s2.name, &s2.age);
	printf("%s %d\n", s1.name, s1.age);
	printf("%s %d\n", s2.name, s2.age);
	return 0;
}

二、位段

1.格式

(1)位段的成员必须是int、unsigned int 或 signed int;
(2)位段的成员名后边有一个冒号和一个数字。
代码如下(示例):

struct S {
	int _a : 2;//_a只占2bit
	int _b : 5;//-b只占5bit
	int _c : 10;//_c只占10bit
	int _d : 30;//_d只占30bit
}s;//8B

2.内存分配

(1)内存分配计算

示例:

struct SS {
	char a : 3;//a只占3bit
	char b : 4;
	char c : 5;
	char d : 4;
}ss;
int main() {
	printf("%d\n", sizeof(struct SS));//3;
	ss.a = 10;//1010->010-> 010
	ss.b = 12;//1100->1100-> 0110 0110
	ss.c = 3;//11->00011-> 0000 0011
	ss.d = 4;//100->0100-> 0000 0100
	//&ss -> 0x 62 03 04
	
	return 0;
}

如图所示:
在这里插入图片描述

三、枚举

枚举:把可能的取值一一列举,如季节,星期,性别等。
关键字:enum。
示例:

enum Season {
	Spring,
	Summer,
	Autumn = 4,
	Winter
};
int main() {
	printf("%d\n", Spring);//0
	printf("%d\n", Summer);//1
	printf("%d\n", Autumn);//4
	printf("%d\n", Winter);//5
	return 0;
}

枚举中的每一个成员都是默认从0开始赋值的。


四、联合(共同体)

联合也是一种特殊的自定义类型,这种类型的变量也包括一系列的成员,特征是这些成员公用一块空间

1.计算

示例:

union Un {
	char c;//1 
	int a;//4
};//由于最大的是4B,所以struct Un的大小至少是4
int main() {
	union Un u;
	printf("%d\n", sizeof(u));//4
	printf("%p\n", &u);//由于共用一块空间,所以这三个地址是相同的
	printf("%p\n", &u.c);
	printf("%p\n", &u.a);
	return 0;
}

2.应用

判断系统的大小端:
(1)给联合体中占用最大空间的a存入1;
(2)输出占用最小空间的c,其只占用1B;
(3)若是返回1系统就是小端,若返回0系统就是大端。

int check_Sys() {
	//检查大小端
	union Un {
		char c;//1
		int a;//4
	}u1;//4
	u1.a = 1;//00 01
	return u1.c; //大端返回0 小端返回1
}
int main() {
	if (check_Sys()) {
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}

	return 0;
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值