结构体,位段,枚举和联合的使用详解

结构体

谈到结构体,我们会关注结构体的声明,结构体的自引用和结构体的大小等问题。接下来我们主要来分析一下结构体的大小是如何计算的,而结构体的大小又是与结构体中的内存对齐息息相关,我们通过对结构体中的内存对齐进行分析来得到结构体的大小。

结构体中的内存对齐

结构体中的内存对齐原则:

  • 第1个成员在与结构体变量偏移量为0的地址处。
  • 从第2个成员开始,就要对齐到“对齐数”的整数倍的地址处了
  • 对于嵌套了结构体的结构体,嵌套的结构体要对齐到 自己的最大对齐数 的整数倍处
  • 结构体的总大小是 最大对齐数 的整数倍(包含嵌套结构体的对齐数)

对齐数:对齐数 = 编译器默认的对齐数该成员大小的较小值(VS中默认对齐数为8,Linux中无默认对齐数)

偏移量的计算

我们可以使用offset宏去计算结构体成员的偏移量。

size_t offsetof( 结构体名称, 成员变量 );

offsetof宏的模拟实现:

我们可以以0为地址,将其强转为结构体类型,然后去访问它的成员变量,因为偏移量 = 成员变量的地址 - 结构体起始位置的地址就是它的偏移量,此时由于结构体的起始地址是0,因此成员变量的地址就是该成员变量的偏移量,最后再进行(int)强转即可。

#define OFFSETOF(struct_name,member_name) (int)&(((struct_name*)0)->member_name)

struct S
{
	char a;
	int b;
	short c;
};

int main()
{
	int ret = OFFSETOF(struct S, b);
	printf("%d\n", ret);
	return 0;
}

修改默认对齐数

我们可以使用 #pragma 预处理指令去改变默认对齐数。(一般我们会将默认对齐数改为2^n)

//此时下面的结构体就是以编译器的默认对齐数为4来进行内存对齐的
#pragma pack(4)
struct S
{
	char a;
	int b;
	char c;
};
//设置过后要进行取消
#pragma pack()

结构体的传参

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

原因:

  • 函数传参的时候,参数是需要压栈的,会有时间和空间上的系统开销。
  • 如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销会比较大,这会导致性能的下降。

位段

位段与结构体是类似的,但是存在2点不同

  1. 位段的成员必须是整形家族:int (unsigned int, signed int) , char, short【不能存在float, double】
  2. 位段的成员名后要加 : 数字

形如:

struct A
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};

位段中的内存分配

位段在开辟空间时候,是按照1个字节 ( char ) 或者4个字节 ( int ) 的方式去开辟的。

但是由于位段存在跨平台问题,所以有很多不确定因素。

位段的跨平台问题

  • int位段被当成有符号数还是无符号数是不确定的
  • 位段中最大位的数目不能确定 (16位机器最大是16,32位机器最大32,32位机器上的20在16位机器上会出问题)
  • 位段中的成员在内存中从左向右分配还是从右向左分配没有定义
  • 当一个结构体有2个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余位,还是利用,无法确定。

枚举

枚举的使用

枚举的优点

  • 使用方便,一次可以定义多个变量
  • 便于调试
  • 防止命名污染
  • 增加了代码的可读性和可维护性
  • 与#define定义的标识符相比,枚举有类型的检查,更加严谨

联合

联合的成员是共用同一块内存空间的,因此对于一个联合变量的大小,它至少是最大成员的大小。

union UN
{
	int i;
	char c;
};

注意:联合的成员i和c是无法同时使用的。

联合大小的计算

  • 联合的大小至少是最大成员的大小
  • 当最大成员的大小不是最大对齐数的整数倍时,要对齐到最大对齐数的整数倍

用联合去判断大小端存储

通过联合体中的int向其存入整形1,然后再通过联合体的char去访问,这样如果是小端存储的话,就会访问到01,如果是大端就是00

union UN
{
	int i;
	char c;
};

int check_sys()
{
	union UN u;
	u.i = 1;
	return u.c;
}

int main()
{
	if (check_sys())
	{
		printf("小端");
	}
	else
	{
		printf("大端");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_51696091/article/details/114518515