C语言中结构体的内存对齐问题

本文详细介绍了C语言中结构体的内存对齐概念及其重要性,包括内存对齐的原理、目的以及如何实现对齐,并提供了避免内存对齐带来的空间浪费的方法。

关于C语言中结构体的内存对齐,要搞清楚四个问题:

1、什么是结构体内存对齐?
2、为什么要内存对齐?
3、如何对齐?
4、如何避免内存对齐的影响?

首先来看第一个问题:什么是结构体内存对齐?

先来看一段代码:

#include <stdio.h>
#include <stddef.h>
struct S
{
    int i;
    char c;
    double d;
};

int main()
{
    struct S s;
    //printf("%d\n", offsetof(struct S, i));
    printf("%d\n", sizeof(s));
    return 0;
}

在windows环境下这段代码的输出结果是多少呢?如果你的答案是13,那么恭喜你成功的掉到了坑里,正确的答案是16!为什么会是16呢?请看下图解析:

这里写图片描述

如上图所示,结构体在内存中的存储方式跟我们想象中的不大一样,而是要遵守一定的原则,这种原则就是内存对齐,具体的原则内容会在下文提到。显而易见的的是结构体这样在内存中存储会浪费一定的空间(如图中黄色区域就是浪费掉的空间),既然浪费了空间,那为什么还要内存对齐?

接着来说第二个问题:为什么要内存对齐?

为什么要内存对齐呢?原因有两个:
1、使用平台的原因:不是在所有平台上都可以访问任意内存的地址,所以需要适当的进行偏移,偏移到我们可以访问的位置上进行数据存储。
2、内存对齐可以使访问速度提高:为了访问未对齐的内存,处理器需要两次内存访问;然而,对齐的内存访问仅需要一次访问。

怎么样来理解第二个原因呢?请看下图:
这里写图片描述

第二个问题理解清楚之后再来看第三个问题:如何对齐?

在此将阐述内存对齐的三个规则:
1、数据成员对齐规则:结构体中的第一个数据成员放在0偏移处,从第二个成员开始,每个数据成员存储的起始位置要从对齐数(该成员自身的大小和默认对齐数的较小值)的整数倍开始;
2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
3、收尾工作:结构体的总大小,必须是其内部最大成员的整数倍,不足的要补齐。
了解清楚规则之后再看第一个问题中所举的例子就比较容易啦

最后我们来看最后一个问题:如何避免内存对齐的影响?

方法1、尽量将结构体中相同类型的数据集中到一起,这样做可以有效的节省空间。
例1:

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

很容易得出该结构体的大小为12;
例2:

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

很容易得出该结构体的大小为8;
可见将相同类型的结构体成员集中到一起可以有效的节省空间。
方法2、使用#pragma pack()来修改编译器的默认对齐数
使用指令#pragma pack(n)将编译器的默认对齐数改为n
使用指令#pragma pack()编译器将取消自定义字节对齐方式
vc/vs环境下的默认对齐数为–8
Liunx环境下的默认对齐数为–8

看如下例子:(在vs环境下)

#pragma pack(1)
struct test1
{
    charchar c2;
    int i;
};
#pragma pack()

ragma pack()
“`;;
很容易得出该结构体的大小为8而不是12

<think>我们首先需要回答C语言结构体内存对齐的规则,然后根据规则提出3-5个相关问题。 回答结构体内存对齐规则: 1. 数据成员对齐规则:结构体struct)的数据成员,第一个数据成员放在偏移量为0的地方,以后每个数据成员的对齐按照对齐参数(这个数据成员自身大小与系统默认对齐参数中较小的那个)的整数倍进行。 也就是说,每个数据成员的起始地址必须是其对齐参数的整数倍。 2. 结构体整体对齐规则:在数据成员完成各自对齐之后,结构体本身也要进行对齐结构体对齐参数取所有成员中对齐参数最大值与系统默认对齐参数中较小的那个,结构体的总大小必须是该对齐参数的整数倍。 3. 如果有嵌套结构体,嵌套的结构体对齐到其自身最大对齐参数的整数倍上(注意:嵌套结构体对齐参数是其内部所有成员对齐参数的最大值,包括嵌套结构体内部的结构体成员),然后整个结构体的大小也要按照最大对齐参数(包括嵌套结构体对齐参数)的整数倍。 另外,可以使用#pragma pack(n)来指定对齐参数n(n通常是1,2,4,8,16),此时每个成员的对齐参数就是自身大小与n的较小值,整体对齐参数也是所有成员对齐参数最大值与n的较小值。 注意:不同编译器可能有细微差别,但以上规则是通用的。 现在,我们按照要求提出3-5个相关问题。</think>### C语言结构体内存对齐规则 在C语言中,结构体内存对齐是为了优化CPU访问效率,遵循以下核心规则: 1. **成员对齐规则** 每个成员的起始地址必须是其**自身大小**与**编译器对齐参数**(可通过`#pragma pack(n)`设置)中**较小者**的整数倍。 - *示例*:`int`(4字节)在4字节对齐下需从4的倍数地址开始 2. **结构体大小规则** 结构体总大小必须是**所有成员最大对齐值**与**编译器对齐参数**中**较小者**的整数倍。 3. **成员顺序影响** 成员声明顺序直接影响内存布局和填充字节数量(编译器不自动重排成员)。 4. **嵌套结构体对齐** 嵌套结构体需按其内部最大对齐对齐(而非整体大小)。 5. **填充字节(Padding)** 编译器自动插入无效字节满足对齐要求: - 成员间填充:确保下一成员正确对齐 - 尾部填充:确保结构体数组时每个元素都对齐 ### 示例解析 ```c #pragma pack(4) // 设置4字节对齐 struct Example { char a; // 1字节 (地址0) // 填充3字节 (地址1-3) int b; // 4字节 (地址4-7) short c; // 2字节 (地址8-9) // 尾部填充2字节 (地址10-11) }; // 总大小:12字节 ``` ### 对齐参数影响 - 使用`#pragma pack(1)`时:无填充,大小=各成员之和 - 默认对齐(如32位Linux):通常按成员自身大小对齐 - 64位系统:常按8字节对齐
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值