内存对齐 联合体 位段式与截断 补码

CPP中的内存对齐

内存对齐的原因之一

计算机硬件通常是按字或双字边界来访问内存的。如果数据没有对齐,可能需要多次内存访问,从而降低了速度。

内存对齐

  1. 结构体对齐:结构体本身的对齐要求通常是其成员中对齐要求最大的那个———可以理解为,结构体大小需要是其成员中最大对齐数的倍数。成员的偏移量,需要是自身和默认对齐数中,较小哪一位的倍数
  • 下方的实验代码为例,double_number的对齐数是8,但存储完int_number后,此时地址的偏移量是3(从0开始),需要填充成员中最大对齐数的倍数处,这里就是8。因此,结构体的实际大小可能大于其成员大小之和。
  1. 联合体对齐:联合体(union)的对齐要求是其成员中对齐要求最大的那个。不过,由于联合体中的所有成员共享同一块内存,因此联合体的大小等于其最大成员的大小。

对齐数的计算

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到对齐数的整数倍的地址处——对齐数 = 编译器默认的对齐数(如VS2019下是8)与 该成员大小的 较小值
  3. 结构体总大小为成员中最大对齐数的整数倍(如标题中24%8==0)。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处

位段式的结构体

  1. 位段是C语言中一种特殊的结构体成员类型,它允许使用比字节更小的单位来存储数据

  2. 位段在结构体中定义时,需要指定一个整数类型(通常为unsigned int或int),并在冒号后面指明位数。位段式结构体通常用于表示具有多个独立标志位或具有较小范围值的数据,从而节省内存空间

例如:

1
2
3
4
5
6
typedef struct {
unsigned int flagA : 1; // 分配1位来存储flagA
unsigned int flagB : 1; // 分配1位来存储flagB
unsigned int field : 6; // 分配6位来存储field
} BitFieldStruct;
//总的一共占1个字节

实验代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#pragma pack(8)

// ① 定义一个名为bitSegmentStruct的位段结构体
// 一次性开辟好,一个字节内部,从低位向高位
typedef struct {
int bit_fi : 3;
int bit_se : 3;
} bitSegmentStruct; // 对齐数4

// 定义一个名为customStruct的结构体,用于满足要求的各种数据类型
typedef struct {
int int_number; // int类型的数字 4<8 取4
double double_number; // double类型的数字 取8
char char_array[3]; // 大小为8的char类型数组 取1,相当于3个char
bitSegmentStruct bit_segment; // bitSegmentStruct类型的结构体成员
} customStruct;

int main() {
// ② 使用malloc函数构建一个customStruct类型的结构体
customStruct* ptr = (customStruct*)malloc(sizeof(customStruct));
if (ptr == NULL) {
printf("内存分配失败!\n");
return 1;
}
memset(ptr, 0, sizeof(customStruct)); // 将堆上这一部分的值初始化

// 为结构体成员赋值(根据需要自行修改)
ptr->bit_segment.bit_fi = 6; // 结构体指针找成员 使用-> 结构体引出成员使用.
ptr->bit_segment.bit_se = 5;
strncpy(ptr->char_array, "xx", 2); // 拷贝字符串,只需要"xx"

// ③ 输出默认对齐数为8的情况下的对齐数字
printf("默认对齐数为8的情况下:\n");
printf("int_number的对齐数字: %zu\n", offsetof(customStruct, int_number));
printf("double_number的对齐数字: %zu\n", offsetof(customStruct, double_number));
printf("char_array的对齐数字: %zu\n", offsetof(customStruct, char_array));
printf("bit_segment的对齐数字: %zu\n", offsetof(customStruct, bit_segment));
printf("bit_fi的实际存储的数字:%d\n", ptr->bit_segment.bit_fi);
printf("bit_se的实际存储的数字:%d\n", ptr->bit_segment.bit_se);
printf("整个ptr结构的大小是:%zu\n", sizeof(*ptr));

// 释放分配的内存
free(ptr);

return 0;
}

实验结果分析

知乎号也是我的...很久没用了

截断,注意负数要使用补码

  1. 符号位:最高位(最左边的一位)表示符号,0 表示正数,1 表示负数。
  2. 数值位:其余的位用于表示数值大小。

110(6):

  • 补码形式,符号位 1 表示负数。
  • 取反 001,加 1 得 010,所以是 -2。

101(5):

  • 补码形式,符号位 1 表示负数。
  • 取反 010,加 1 得 011,所以是 -3。

内存对齐 联合体 位段式与截断 补码
https://weihehe.top/2024/07/05/内存对齐/
作者
weihehe
发布于
2024年7月5日
许可协议