【C语言】结构体内存对齐机制详解

一、前言

在讲解结构体内存对齐机制之前,我们先来看1个例子:

typedef struct
{
    char sex;       //  性别
    int id;         //  学号
    char name[20];  //  姓名
    float score;    //  成绩
    char addr[30];  //  地址
}STU;

int main(){
    STU student1;
    printf("%d\n",sizeof(student1));//  打印结构体变量所占内存长度
    return 0;
}

以上代码示例中定义了1个结构体student1,包含5个不同数据类型的成员,分别占用内存空间:
char sex:1 Byte
int id:4 Byte
char name[20]:20 Byte
float score:4 Byte
char addr[30]:30 Byte
根据结构体特性,结构体变量所占内存长度等于其各成员所占内存长度之和,因此这里打印的sizeof(student1)是否等于1+4+20+4+30=59呢?运行程序后结果如下:

size of student1: 64

为什么这里结构体变量student1所占内存长度是64而不是59呢?因为有结构体内存对齐机制的存在。这是很多新手程序猿很容易走进的一个误区,接下来我们一起来了解结构体对齐机制。

二、结构体内存对齐规则

  1. 结构体第一个成员的地址即为该结构体变量的地址。
  2. 结构体成员内存对齐时,其存放的内存单元大小为min{有效对齐值,指定对齐值}的最小整数倍。
    注意:
    自身对齐值: 结构体变量里每个成员自身所占内存大小
    指定对齐值: 使用宏定义#pragma pack(N) 其中N的值即为指定对齐值。N必须是2的幂次方,例如:1,2,4,8等,如果没有使用宏定义指定对齐值,则指定对齐值取决于操作系统位数,32位操作系统默认指定对齐值为4,64位操作系统默认指定对齐值为8
    有效对齐值: 有效对齐值为min{自身对齐值,指定对齐值}。
  3. 结构体总占用内存大小为min{成员最大自身对齐值,指定对齐值}的整数倍。

三、实例解析

以下示例代码均在64位Windows操作系统下运行
1、使用默认指定对齐值
使用文章前言的代码分析:

#include <stdio.h>

typedef struct
{
    char sex;       //  性别
    int id;         //  学号
    char name[20];  //  姓名
    float score;    //  成绩
    char addr[30];  //  地址
}STU;

int main(){
    STU student1;
    printf("size of student1: %d\n",sizeof(student1));    //  打印结构体变量所占内存长度
    return 0;
}
成员自身对齐值指定对齐值有效对齐值
sex18min{1,8}
id48min{4,8}
name[20]18min{1,8}
score48min{4,8}
addr[30]18min{1,8}

结构体总体对齐时,应按照成员自身对齐值的最大值的整数倍对齐,这里程序在没有宏定义指定默认对齐值的情况下,对齐值就应该是min{4,8},也就是4字节,这种情况下,该结构体每一个成员开辟的存储空间都为4字节。

成员实际占用内存大小系统开辟内存空间大小
sex14
id44
name[20]2020
score44
addr[30]3032

在这里插入图片描述

成员id、name[20]、score实际占用内存大小刚好是对齐值4的整数倍,而成员sex则需要开辟4字节空间,实际只占用1字节,剩余3字节未使用;成员addr[30]需要开辟32字节空间,实际使用30字节,剩余2字节未使用。因此打印结构体变量student1所占内存大小为64字节。

2、修改默认指定对齐值
在上述示例基础之上修改默认指定对齐值:

#include <stdio.h>
#pragma pack(1) //  指定默认对齐值为1
typedef struct
{
    char sex;       //  性别
    int id;         //  学号
    char name[20];  //  姓名
    float score;    //  成绩
    char addr[30];  //  地址
}STU;
#pragma pack()  //  取消修改对齐值
int main(){
    STU student1;
    printf("size of student1: %d\n",sizeof(student1));    //  打印结构体变量所占内存长度
    return 0;
}

此时程序输出结果为:

size of student1: 59

以上代码使用#pragma pack(1) 将默认指定对齐值修改为1,那么结构体成员将会按照1字节做内存对齐。

成员实际占用内存大小系统开辟内存空间大小
sex11
id44
name[20]2020
score44
addr[30]3030

此时打印结构体变量student1所占内存长度就为59。

如果将默认指定对齐值修改为2呢?

#include <stdio.h>
#pragma pack(2) //  指定默认对齐值为1
typedef struct
{
    char sex;       //  性别
    int id;         //  学号
    char name[20];  //  姓名
    float score;    //  成绩
    char addr[30];  //  地址
}STU;
#pragma pack()  //  取消修改对齐值
int main(){
    STU student1;
    printf("size of student1: %d\n",sizeof(student1));    //  打印结构体变量所占内存长度
    return 0;
}

程序输出结果:

size of student1: 60
成员实际占用内存大小系统开辟内存空间大小
sex12
id44
name[20]2020
score44
addr[30]3030

这个时候除了成员sex之外,其余成员的自身对齐数均是2的整数倍,因此成员sex需要根据对齐数2进行内存对齐,开辟2字节空间存储,但实际只使用1字节空间,最后打印结构体变量student1的所占内存长度为60。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言中,结构体内存对齐是为了提高存储器的访问效率。结构体的成员在内存中的布局可能会按照一定规则进行对齐,以保证访问成员时的效率和正确性。 内存对齐是为了满足硬件对数据的访问要求,比如某些平台要求访问某些类型的数据必须从特定地址开始。此外,对齐也有助于减少内存碎片和提高内存的利用率。 在C语言中,默认情况下,结构体的成员按照其声明的顺序依次存放在内存中,但是编译器可能会在结构体中添加一些填充字节,以保证结构体的对齐要求。 编译器会根据结构体成员的类型和顺序来确定对齐方式,常见的对齐规则有以下几种: 1. 自然对齐:结构体成员按照其自身的大小进行对齐。例如,一个int类型的成员会按照4字节对齐。 2. 最大对齐:结构体成员按照其成员中最大类型的大小进行对齐。例如,一个结构体中有int和char类型成员,那么整个结构体会按照int类型的大小进行对齐。 3. 指定对齐:通过编译器提供的特定语法,可以手动指定结构体成员的对齐方式。 结构体的对齐方式可以通过编译器的相关选项进行配置,比如gcc编译器可以使用`__attribute__((aligned(x)))`来指定对齐方式,其中x表示对齐的字节数。 需要注意的是,结构体的对齐方式可能会因编译器、编译选项和目标平台而有所不同。因此,在编写跨平台代码时,应尽量避免依赖结构体的具体内存布局和对齐方式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值