结构体,联合体与位段

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

1.1 为什么需要结构体内存对齐?

原因1:平台原因 

不是所有的硬件平台都能访问任意地址上的任意数据的;某些平台只能在某些地址处取得某些特定类型的数据,否则抛出硬件异常。
比如,当一个平台要取一个整型数据时只能在地址为4的倍数的位置取得,那么这时就需要内存对齐,否则无法访问到该整型数据。

原因2: 性能原因

数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐内存,处理器需要作两次内存访问;而对齐的内存访问仅需一次。比如说下面这个整形i,我们想在一个32位机器访问它,一次读取四个字节,就需要两次才能访问完整的i,如果对齐了就只需要一次访问即可.

结构体,联合体与位段_第1张图片

1.2 结构体内存对齐规则 

struct S {
	char c;
	int i;
	double d;
};

1.算出每个成员的对齐数(VS的默认对齐数是8,对齐数就是本身成员变量)

结构体,联合体与位段_第2张图片

2.将每个元素对齐到较小值的整数倍下面.

结构体,联合体与位段_第3张图片

3.通过最大对齐数知道大小最后的大小,比如说这个结构体的大小一定要是最大对齐数的整数倍,最大对齐数就是上面求出的较小值中的最大值.(是8),此时大小是16,正好是8的倍数,大多数情况下,此时的大小并不是最大对齐数的整数倍,这个时候我们只需补齐即可. 

1.3 设计结构体的技巧 

其实在我们设计结构体的时候,如果结构体成员的顺序设计得合理的话,是可以避免不必要的内存消耗的。
两个结构体的成员变量相同,但是成员变量的顺序不同,可能就会出现结构体的大小不同的情况: 

struct S1
{
	char a;
	char b;
	int c;
};

struct S2
{
	char a;
	int c;
	char b;
};

这个时候我们发现,结构体S1占用的是8个字节,而结构体S2就占用了12个字节,所以我们在设计结构体的时候需要将占用内存小的元素放在一块儿,可以避免内存的浪费.

1.4 修改默认对齐数 

在VS2022中,我们可以自由的修改默认对齐数来满足我们的内存需求.

#pragma pack()

只需在pack中填入你设置的默认对齐数即可.

2.联合体 

2.1 什么是联合体

在C语言中,存在这样一个类型,可以让多个变量共用一块内存,这就是联合体.

使用场景:在某个变量不使用的时候可以使用其他变量.

缺点:在使用其他变量的时候会改变内存中的数据

union 联合名
{
成员表
};

2.2 联合体的创建方式(和结构体相似)

1.先创建模板,再定义变量

// 创建联合体模板union u
union u
{
    int i;
    char c;
};
// 使用该联合体模板创建两个变量a, b
union u a,b;

2.创建模板时同时定义变量

// 创建联合体模板union u的同时定义两个变量a、b
union u
{
    int i;
    char c;
};

3.匿名联合体

union
{
    int i;
    char c;
}a,b;

4.typedef

typedef union u
{
    int i;
    char c;
}U;
U a = {1,'a'};

2.3 初始化

U a;
a.i = 10;
U b = a;				        /* 1、把一个联合初始化为另一个同类型的联合; */
U c = {20};				        /* 2、初始化联合的第一个成员; */
U d = {.i = 30};                /* 3、根据C99标准,使用指定初始化器。 */

2.4 应用:判断机器的大小端

typedef union u
{
    int i;
    char c;
}U;
U u;
u.i = 0x12345678
//打印c,如果是78就是小端,如果是12就是大端

3.位段

3.1 什么是位段

位段是通过结构体来实现的一种以位(bit位)为单位的数据存储结构,它可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作.这样可以节省空间.

注:位段里面的成员只能有整形家族来组成,否则会报错.位段后面分配的比特位不能超过原本类型的大小.

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

3.2 位段内存计算

我们以这个结构体举例

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

实际上结果是3个字节,我们画图来看一下,这样相对直接使用结构体来说还是节省了不少空间的.

结构体,联合体与位段_第4张图片

3.3 位段的跨平台问题

1.int型位段成员会被当成有符号数还是无符号数是不确定的
2.位段中最大位数目是不确定的(在16位机器上int型最大为16,而在32为机器上int型最大为3.32,如若写成27,那么16位机器就会出问题)
4.位段的成员在内存中到底是从左向右分配,还是从右向左分配尚未定义
5.当一个结构体包含两个位段,第二个位段比较大,无法容纳于第一个位段剩余的位时, 是舍弃剩余的位还是利用,是不确定的。
综上所述:位段是不跨平台的

你可能感兴趣的:(C语言学习,数据结构)