c语言中结构体的大小

C语言中结构体(struct)的大小计算涉及到几个关键概念,包括数据对齐(alignment)、填充(padding)、以及可能的尾部填充。结构体的总大小不仅取决于其成员的大小,还受到成员的排列顺序和编译器的对齐要求的影响。

数据对齐

数据对齐是指结构体中每个成员相对于结构体起始地址的偏移量必须是该成员大小的整数倍。这是为了提高内存访问效率。例如,如果一个[int](file:///home/book/APUE/solution.cpp#8%2C5-8%2C5)类型的成员大小为4字节,那么它的地址偏移量应该是4的倍数。

填充(Padding)

为了满足数据对齐的要求,编译器可能会在结构体成员之间或结构体末尾插入额外的空间(填充)。这意味着结构体的实际大小可能大于其所有成员大小的总和。

尾部填充

即使在结构体的最后一个成员之后,也可能添加填充,以确保结构体的总大小是某个数(通常是结构体中最大成员大小)的倍数。

示例 1

假设有以下结构体:

struct Example {
    char a;    // 字符型占用1字节
    int b;     // 整型通常占用4字节
    char c;    // 字符型占用1字节
};

在大多数情况下,编译器对int类型的对齐要求是4字节。因此,上述结构体的大小计算如下:

  1. char a;占用1字节,之后为了满足int b;的对齐要求,会插入3字节的填充。
  2. int b;占用4字节。
  3. char c;占用1字节,之后可能还会有3字节的尾部填充,以确保结构体大小是4的倍数(这取决于具体的编译器和编译器的设置)。

因此,这个结构体的总大小可能是12字节。

示例2

struct stu {
    char a;      // 字符型占用1字节
    short int b; // 短整型通常占用2字节
    int c;       // 整型通常占用4字节
} a;

计算时需要考虑到编译器的数据对齐规则,这些规则可能因编译器和目标平台的不同而有所差异。以下是一个基于常见对齐规则的计算过程:

  1. char a; 占用1字节。之后,为了满足short int b;的对齐要求(通常是2字节对齐),编译器会在ab之间插入1字节的填充。

  2. short int b; 占用2字节。

  3. int c; 由于int类型通常要求4字节对齐,而在b之后,当前总大小为4字节(char a + 1字节填充 + short int b),所以可以直接紧跟b存放int c,无需额外填充。

因此,结构体stu的总大小为:

  • char a; -> 1字节
  • 填充 -> 1字节
  • short int b; -> 2字节
  • int c; -> 4字节
  • 总计:8字节

示例3

struct Test1
{
    char  c1;
    short s;
    char  c2;
    int   i;
};
struct Test2
{
    char  c1;
    char  c2;
    short s;
    int   i;
};

结构体 Test1

  1. char c1; 占用1字节,之后为了满足short s;的对齐要求(通常是2字节对齐),编译器会在c1s之间插入1字节的填充。
  2. short s; 占用2字节。
  3. char c2; 占用1字节,之后为了满足int i;的对齐要求(通常是4字节对齐),编译器会在c2i之间插入3字节的填充。
  4. int i; 占用4字节。

结构体Test1的总大小为:1 + 1(填充)+ 2 + 1 + 3(填充)+ 4 = 12字节。

结构体 Test2

  1. char c1; 占用1字节。
  2. char c2; 紧接着c1占用1字节,两个char类型连续放置不需要填充。
  3. short s; 由于short通常要求2字节对齐,当前总大小为2字节(char c1 + char c2),所以可以直接紧跟c2存放short s,无需额外填充。
  4. int i; 由于int类型通常要求4字节对齐,而在s之后,当前总大小为4字节,所以可以直接紧跟s存放int i,无需额外填充。

结构体Test2的总大小为:1 + 1 + 2 + 4 = 8字节。

这个例子展示了如何通过重新排列结构体成员的顺序来减少因对齐引入的填充,从而减小结构体的总大小。实际的大小可能因编译器和目标平台的不同而有所变化,但通常情况下,Test2由于更有效的成员排列,会比Test1有更小的总大小。**

测试验证

#include <stdio.h>
struct Example {
    char a;    // 字符型占用1字节
    int b;     // 整型通常占用4字节
    char c;    // 字符型占用1字节
};
struct stu {
    char a;      // 字符型占用1字节
    short int b; // 短整型通常占用2字节
    int c;       // 整型通常占用4字节
} ;
struct Test1 {
    char c1;
    short s;
    char c2;
    int i;
};

struct Test2 {
    char c1;
    char c2;
    short s;
    int i;
};

int main() {
    printf("Size of Example: %zu bytes\n", sizeof(struct Example));
    printf("Size of stu: %zu bytes\n", sizeof(struct stu));
    printf("Size of Test1: %zu bytes\n", sizeof(struct Test1));
    printf("Size of Test2: %zu bytes\n", sizeof(struct Test2));
    return 0;
}

在这里插入图片描述

计算技巧

  • 结构体的起始地址总是对齐的。
  • 结构体的大小是其最宽基本成员大小的倍数。
  • 重新排列结构体成员的顺序可能减少因对齐引入的填充,从而减小结构体的总大小。

注意

实际的对齐规则和填充量可能因编译器和目标平台的不同而有所差异。可以通过编译器特定的属性(如GCC的__attribute__((packed)))来控制对齐行为,但这可能会影响性能或引起其他问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值