struct 大小计算

结构体是一种复合数据类型,通常编译器会自动的进行其成员变量的对齐,已提高数据存取的效率。在默认情况下,编译器为结构体的成员按照自然对齐(natural alignment)条方式分配存储空间,各个成员按照其声明顺序在存储器中顺序存储。自然对齐是指按照结构体中成员size最大的对齐,在cl编译器下可以使用

#pragma pack(n)

来指定结构体的对齐方式。

默认对齐方式

在默认对齐方式下,结构体成员的内存分配满足下面三个条件

  1. 结构体第一个成员的地址和结构体的首地址相同
  2. 结构体每个成员地址相对于结构体首地址的偏移量(offset)是该成员大小的整数倍,如果不是则编译器会在成员之间添加填充字节(internal adding)。
  3. 结构体总的大小要是其成员中最大size的整数倍,如果不是编译器会在其末尾添加填充字节(trailing padding)。

下面是一个示例:

struct s1{
    char ch;
    int a;
    double b;
    char c1;
};

struct s2{
    char ch;
    int a;
    double b;
};

int main()
{
    cout << "s1的大小: " << sizeof(struct s1) << endl;
    cout << "ch的地址偏移是   " << offsetof(s1, ch) << endl;
    cout << "a 的地址偏移是   " << offsetof(s1, a) << endl;
    cout << "b 的地址偏移是   " << offsetof(s1, b) << endl;
    cout << "c1的地址偏移是   " << offsetof(s1, c1) << endl;

    cout << "=====================================" << endl;
    cout << "s2的大小: " << sizeof(struct s2) << endl;
    cout << "ch的地址偏移是   " << offsetof(s2, ch) << endl;
    cout << "a 的地址偏移是   " << offsetof(s2, a) << endl;
    cout << "b 的地址偏移是   " << offsetof(s2, b) << endl;
    getchar();

    return 0;
}

代码中 offsetof函数可以得到结构体成员相对于该结构体首地址的偏移量。
其运行结果如下图
这里写图片描述

对于结构体s1来说,

  1. ch是其第一个成员故其地址和结构体的地址是相同的也就是说偏移量为0;
  2. a是int型其大小为4个字节,按照条件(2) 结构体每个成员地址相对于结构首地址的偏移量(offset)是该成员大小的整数倍,如果不是则编译器会在成员之间添加填充字节,所以其地址偏移应该是4,也就说编译器在第一个成员ch后面填充了3个字节。
  3. b是double型占8个字节,其地址偏移应该是8的整数倍,由于a的地址偏移是4其大小为4个字节,正好b的偏移地址是8,不需要填充字节。
  4. c1是char型占1个字节,偏移地址是16(b的偏移地址是8大小也是8,中间也没有填充字节)。
  5. 这时成员ch占1个字节后面有3个字节的填充,a占4个字节后面无填充,b占8个字节后面无填充,c1占1个字节,s1总的大小是 1+3+4+8+1=17 1+3+4+8+1=17。按照条件(3)结构体总的大小需是其最大成员所占空间的整数倍,其最大的成员b占有8字节,17显然是不符合条件的,所以需要在结构体的末尾填充7个字节,最后结构体总的大小是24字节
    结构体s2和s1的成员是非常相似的,唯一的区别是其末尾没有最后7个字节的填充,所以其大小是16个字节,这里用于和s1做对比说明s1末尾的填充字节。

指定对齐方式

可以使用#pragma pack(N)来指定结构体成员的对齐方式
对于指定的对齐方式,其成员的地址偏移以及结构的总的大小也有下面三个约束条件

  1. 结构体第一个成员的地址和结构体的首地址相同
  2. 结构体每个成员的地址偏移需要满足:N大于等于该成员的大小,那么该成员的地址偏移需满足默认对齐方式(地址偏移是其成员大小的整数倍);N小于该成员的大小,那么该成员的地址偏移是N的整数倍
  3. 结构体总的大小需要时N的整数倍,如果不是需要在结构体的末尾进行填充。
  4. 如果N大于结构体成员中最大成员的大小,则N不起作用,仍然按照默认方式对齐。

示例仍然是上面的s1和s2,不过使用

#pragma pack(4)

设定按照4字节对齐
运行结果
这里写图片描述
结果分析

  1. ch是其第一个成员故其地址和结构体的地址是相同的也就是说偏移量为0;
  2. a占4个字节,和设定的对齐方式相等,所以其地址偏移是其大小的整数倍为4。
  3. b占8个字节,大于设定的对齐方式4,所以其地址偏移是N的整数倍为8。
  4. c1占1个字节,小于设定的对齐方式,所以其地址偏移是其大小的整数倍为16。
  5. 总的大小17个字节,不是N(4)的整数倍,所以在结构体的末尾填充3个字节,总的大小为20个字节。

说明:

  1. 在使用#pragma pack设定对齐方式一定要是2的整数幂,也就是(1,2,4,8,16,...),不然不起作用的,仍然按照默认方式对齐。
  2. 当结构体中有其他的结构体作为成员时,计算最大成员是不能把结构体成员作为一个整体来计算,要看其每个成员的大小。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
结构体的大小是由其成员的大小和对齐规则所确定的。下面是一个详细的图解来说明结构体大小计算过程: ``` +-------------+ | | | struct | | | +-------------+ | | | member1 | | | +-------------+ | | | member2 | | | +-------------+ | | | member3 | | | +-------------+ ``` 在这个示意图中,`struct` 表示结构体的起始地址,而 `member1`、`member2` 和 `member3` 分别表示结构体的三个成员。 结构体的大小是成员大小的总和,但还要考虑对齐规则。对齐规则是由编译器决定的,它确保结构体的每个成员在内存中按照一定的对齐方式排列。 对于大多数平台和编译器,对齐规则通常是将成员按照其自身大小进行对齐。例如,如果一个成员的大小是4字节,那么它通常会在内存中以4字节对齐。 对齐规则还要求结构体的起始地址必须是某种特定的对齐边界。这个对齐边界通常是结构体中最大成员的大小。例如,如果结构体中最大的成员大小是8字节,那么结构体的起始地址通常会以8字节对齐。 在计算结构体大小时,按照对齐规则将每个成员的大小进行对齐,并按照声明的顺序依次排列。这样,结构体的大小就是成员大小的总和,加上必要的对齐填充。 下面是一个示例来说明结构体大小计算过程: ```c struct Example { char c; int i; double d; }; ``` 在这个示例中,`Example` 结构体有三个成员:`c`、`i` 和 `d`。 假设 `char` 类型大小为1字节,`int` 类型大小为4字节,`double` 类型大小为8字节。 根据对齐规则,`c` 成员不需要对齐填充,`i` 成员需要进行4字节对齐填充,而 `d` 成员需要进行8字节对齐填充。 所以,结构体 `Example` 的大小计算如下: ``` sizeof(struct Example) = sizeof(char) + 3字节填充 + sizeof(int) + sizeof(double) + 4字节填充 = 1 + 3 + 4 + 8 + 4 = 20 字节 ``` 因此,结构体 `Example` 的大小为20字节。 希望这个图解能够帮助你更好地理解结构体大小计算过程。请注意,对齐规则可能因平台、编译器和编译选项而有所不同。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值