C中的内存字节对齐问题(修改补充版)

C语言面试和考试中经常会遇到内存字节对齐的问题。今天就来对字节对齐的知识进行小结一下。

首先说说为什么要对齐。为了提高效率,计算机从内存中取数据是按照一个固定长度的。以32位机为例,它每次取32个位,也就是4个字节(每字节8个位,计算机基础知识,别说不知道)。字节对齐有什么好处?以int型数据为例,如果它在内存中存放的位置按4字节对齐,也就是说1个int的数据全部落在计算机一次取数的区间内,那么只需要取一次就可以了。如图a-1。如果不对齐,很不巧,这个int数据刚好跨越了取数的边界,这样就需要取两次才能把这个int的数据全部取到,这样效率也就降低了。

                          

图:a-1                                    

  

    图:a-2

内存对齐是会浪费一些空间的。但是这种空间上得浪费却可以减少取数的时间。这是典型的一种以空间换时间的做法。空间与时间孰优孰略这个每个人都有自己的看法,但是C语言既然采取了这种以空间换时间的策略,就必然有它的道理。况且,在存储器越来越便宜的今天,这一点点的空间上的浪费就不算什么了。

需要说明的是,字节对齐不同的编译器可能会采用不同的优化策略,以下以GCC为例讲解结构体的对齐.

一、原则:

1.若没有指定有效对齐值,结构体内成员按自身按自身长度自对齐。

自身长度,如char=1,short=2,int=4,double=8,。所谓自对齐,指的是该成员的起始位置的内存地址必须是它自身长度的整数倍。如int只能以0,4,8这类的地址开始

2.若指定了有效对齐值,则按有效对齐值对齐。

3.结构体的总大小为结构体的有效对齐值的整数倍

当未明确指定有效对齐位时,以结构体中最长的成员的长度为其有效值;

结构体的有效对齐值的确定:

2)当用#pragma pack(n)指定时, 成员将在作为 n 的倍数或成员的大小的倍数的边界(以较小者为准)上对齐(这句是官方的话,这里意思是:每个成员自身的字节大小与n比较,较少者成为每个成员自身的有效对齐值)

3)当用__attribute__ ((__packed__))指定长度时,强制按照此值为结构体的有效对齐值

二、例子

1。

struct AA{

    char a;

    int b;

    char c; 

}aa

结果,sizeof(aa)=12

何解?首先假设结构体内存起始地址为0,那么地址的分布如下

0  a

1  

2

3

4  b

5  b

6  b

7  b

8  c

9

10

11

char的字对齐长度为1,所以可以在任何地址开始,但是,int自对齐长度为4,必须以4的倍数地址开始。所以,尽管1-3空着,但b也只能从4开始。再加上c后,整个结构体的总长度为9,结构体的有效对齐值为其中最大的成员即int的长度4,所以,结构体的大小向上扩展到12,即9-11的地址空着。

2.

struct AA{

    char a;

char c; 

    int b;    

}aa

sizeof(aa)=8,为什么呢

0  a

1  c

2

3

4  b

5  b

6  b

7  b

因为c为char类型,字对齐长度为1,所以可以有效的利用1-3间的空格。看见了吧,变量定义的位置的不同时有可能影响结构体的大小的哦!


3.

#pragma pack(2)

struct AA{

    char a;

    int b;

    char c; 

}aa

sizeof(aa)=8,

为什么呢?a到c只占7字节长度,因为结构体的有效对齐长度在pack指定的2和int的4中取

较小的值2。故取2的倍数8。

如果当pack指定为8呢?那就仍然按4来对齐,结果仍然是12。


4.

struct AA{

    char a;

    int b;

    char c; 

}__attribute__((__8__))aa

sizeof(aa)=16,)

为市民?其实a到c仍然只占9字节长度,但结构体以8对齐,故取8的倍数16.

如果其指定2,则结果为10


如果pragma pack和__attribute__ 同时指定呢?以__attribute__ 的为准。

需要说明的是,不管pragma pack和__attribute__如何指定,结构体内部成员的自对齐仍然按照其自身的对齐值。


另外,不同的编译器可能会对内存的分布进行优化,

例如有些编译器会把立体1中的程序优化成例题2的样子。但这属于编译器的问题,

这里不做详细讨论。如果要作为编程的参考的话,最好当做编译器不会做优化,

尽量在保持代码清晰的情况下,自己手动将例题1优化成例题2的样子。

如果是做题的话,按照以上原则做就可以了,不用考虑不同编译器的特性。


本篇是笔者从各路大咖的博客学习后,经过实践和官方文档查阅,多次修改后的结果,虽然已经多次修改,但是仍然

不保证100%正确,所以只作大家学习内存字节对齐问题的参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C++,结构体和类都是由多个成员变量组成的。为了在内存高效地存储这些成员变量,编译器会对结构体和类进行内存字节对齐内存字节对齐是指将结构体或类的成员变量按照一定的规则排列,使得每个成员变量的内存地址都是其长度的整数倍。这样一来,访问这些成员变量时就可以减少内存访问次数,提高访问效率。 内存字节对齐的规则如下: 1. 结构体或类的起始地址必须是其最宽基本类型成员的整数倍。 2. 结构体或类的每个成员变量相对于起始地址的偏移量必须是其类型大小的整数倍。 3. 结构体或类的总大小必须是其最宽基本类型成员大小的整数倍。 例如,一个结构体有两个成员变量,一个是int类型,一个是char类型。如果按照默认的字节对齐规则排列,结构体的内存布局如下: ``` struct MyStruct { int a; char b; }; // 内存布局 // +---+---+ // | a | b | // +---+---+ ``` 这里,int类型占用4个字节,char类型占用1个字节。因此,编译器会按照4字节对齐的方式排列结构体。由于int类型是最宽的基本类型,所以结构体的起始地址必须是4的倍数,而char类型则放在了4字节边界上。 需要注意的是,内存字节对齐规则可能会因为编译器的不同而产生变化。有些编译器允许开发者通过预处理指令来指定结构体的对齐方式,例如: ``` struct MyStruct { int a; char b; } __attribute__((aligned(8)))); ``` 这里,`__attribute__((aligned(8)))`表示MyStruct结构体需要以8字节对齐的方式排列。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值