C语言之内存对齐

在计算结构体的大小时,内存对齐规则起着重要作用。不同的数据类型在内存中可能需要按照特定的地址对齐方式来存储,这样可以提高访问速度。但是,通过 #pragma pack 指令,可以改变默认的对齐方式。我们分别讨论两种情况下 sizeof(struct fun) 的大小。

情况 1:默认

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

在这个结构体中,数据类型的大小和对齐方式如下(假设在大多数 64 位系统上):

  • int 的大小是 4 字节,通常需要 4 字节对齐。
  • double 的大小是 8 字节,通常需要 8 字节对齐。
  • char 的大小是 1 字节,不需要特殊对齐。
内存布局(假设默认对齐规则):
  1. int i 占 4 字节,起始地址对齐到 4 字节边界。
  2. double d 占 8 字节,紧接在 int i 之后,由于 double 要求 8 字节对齐,因此在 int 后需要 4 字节的填充(padding),使得 double 的地址对齐到 8 字节边界。
  3. char c 占 1 字节,紧接在 double d 之后,但为了保持结构体大小是 double 的对齐要求,也会有 7 字节的填充,使得整个结构体的大小是 double 对齐的倍数。
内存布局图:

| int (4 bytes) | padding (4 bytes) | double (8 bytes) | char (1 byte) | padding (7 bytes) |
计算:
  • int i 占 4 字节 + 4 字节填充。
  • double d 占 8 字节。
  • char c 占 1 字节 + 7 字节填充。

因此,sizeof(struct fun) 的大小为 4 + 4 + 8 + 1 + 7 = 24 字节。

情况 2:使用 #pragma pack(1) 时

#pragma pack(1)
struct fun {
    int i;
    double d;
    char c;
};

#pragma pack(1) 指令告诉编译器禁用默认的对齐方式,所有成员按照 1 字节对齐。也就是说,每个数据成员紧密相邻地存储,不会有额外的填充字节。

内存布局:
  1. int i 占 4 字节,按 1 字节对齐。
  2. double d 占 8 字节,紧接在 int 之后。
  3. char c 占 1 字节,紧接在 double 之后。
内存布局图:

| int (4 bytes) | double (8 bytes) | char (1 byte) |
计算:
  • int i 占 4 字节。
  • double d 占 8 字节。
  • char c 占 1 字节。

没有填充字节,因此 sizeof(struct fun) 的大小为 4 + 8 + 1 = 13 字节。

结论

  • 没有 #pragma pack(1) 的情况下,sizeof(struct fun) 是 24 字节(由于内存对齐和填充)。
  • 使用 #pragma pack(1) 时,sizeof(struct fun) 是 13 字节(没有填充,按 1 字节对齐)。

例3:

//64位
struct C
{
double t; //8 1111 1111
char b; //1 1
int a; //4 0001111
short c; //2 11000000
};
sizeof(C) = 24; //注意:1 4 2 不能拼在一起

在这个问题中,涉及到内存对齐的概念。不同类型的数据有不同的对齐要求,以保证处理器能够高效地访问这些数据。64位系统中,通常有如下对齐规则:

  1. double 类型 通常需要 8 字节对齐(即它的地址必须是 8 的倍数)。
  2. char 类型 通常需要 1 字节对齐(即任何地址都可以存储 char)。
  3. int 类型 通常需要 4 字节对齐(即它的地址必须是 4 的倍数)。
  4. short 类型 通常需要 2 字节对齐(即它的地址必须是 2 的倍数)。

由于结构体中的成员变量需要按照它们的对齐规则对齐,因此编译器会在某些成员之间插入填充字节来确保每个成员都能对齐到合适的边界上。

我们逐步分析结构体 C 中每个变量的内存分布情况:

1. double t 的对齐

  • t 是一个 double 类型,通常需要 8 字节对齐。
  • 在 64 位系统上,struct C 的第一个成员通常从地址 0 开始,因此 t 占用 8 字节,地址范围是 0 ~ 7
  • 接下来编译器将为下一个成员变量选择起始地址。

2. char b 的对齐

  • b 是一个 char 类型,通常不需要对齐,可以放在任何地址上。
  • b 将紧接着 t 存储在地址 8(因为地址 8 是 t 后面的第一个空闲字节)。
  • b 占用 1 字节,地址范围是 8 ~ 8
  • 下一个成员变量将从地址 9 开始。

3. int a 的对齐

  • a 是一个 int 类型,通常需要 4 字节对齐。
  • 地址 9 不是 4 的倍数,因此编译器会在 b 后面插入 3 个填充字节,确保 a 能够对齐到 4 字节的边界(地址 12)。
  • a 占用 4 字节,地址范围是 12 ~ 15

4. short c 的对齐

  • c 是一个 short 类型,通常需要 2 字节对齐。
  • 地址 16 是 2 的倍数,因此 c 可以从地址 16 开始。
  • c 占用 2 字节,地址范围是 16 ~ 17
  • 然而,为了让整个结构体的大小是 8 字节的倍数(通常结构体的大小也会对齐到其最大成员的对齐单位),编译器会在 c 后面插入 6 个填充字节,使整个结构体的大小变为 24 字节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值