[牛人杂谈]

结构体的内存分配

[复制链接]
1269|5
手机看帖
扫描二维码
随时随地手机跟帖
huahuagg|  楼主 | 2018-10-28 16:58 | 显示全部楼层 |阅读模式
结构体的内存分配,我个人是觉得比较蛋疼的,它有一个需要遵循的原则,地址对齐,也有人称为内存对齐,叫法没关系,反正我只是“拿来”,会用就行。

       好了,先有这么一个概念,什么是内存对齐,先丢一边。……~(~o ̄▽ ̄)~o 。。。滚来滚去……o~(_△_o~) ~。。



       设char占用1个字节,int占用4个字节。

       那么问题1



struct A

{

       char a;

       int a0;

}ma;



char a1;

a1 = sizeof(ma);
837565bd579f47ff40.png
?_?,为什么是8???,不是5?

是的,不是5,在编译器里面,为了非连续变量取得的地址能整除于结构体的基地址(结构体的分配的基地址也需要满足可以整除于所占用的内存的最大数据类型的字节数,这句话好绕啊_(:3」∠)_),偏移地址需要对齐。就是说在给变量分配的时候,如果不是连续定义的变量,直接分配一个最大的内存空间,即使我给a0变量定义的是一个char型变量,编译器依旧给其分配了4个字节。

关于偏移地址对齐的说法,我个人倾向于同意的说法是,在给结构体分配内存空间时,如果变量地址不连续(注意这句话,很重要),编译器宁可取结构体中内存占用最大的元素为每个变量分配内存,不够的空间予以补足,只是调用的时候不用而已。

下面结合*.map文件分析一下各个变量的内存分配

    ma                                       0x2000002c   Data           8  main.o(.data)
    a2                                       0x20000034   Data           1  main.o(.data)
    a3                                       0x20000035   Data           1  main.o(.data)
    a4                                       0x20000036   Data           1  main.o(.data)
    a5                                       0x20000038   Data           4  main.o(.data)

上面是各个变量的地址分配,我没有在*. map文件中找到 ma.a0 和 ma.a1 的变量地址,(~ ̄▽ ̄~) 那又怎样,不就绕个弯嘛。
99735bd57a094bb62.png
<( ̄ c ̄)y▂ξ,现在可以分析了。

在上图中我通过一个指针变量将ma.a1的地址取了出来,然后执行到红点处停止,结合*.map文件分析,a2地址为0x20000034,ma地址为0x2000002c ,两者相减为8,就是ma的内存分配大小,然后我之前说过,结构体中变量的内存的分配是直接每个变量分配一个所有元素中最大的内存占用元素的,也就是说ma .a0也占用了4个字节。

现在,验证方法就是用ma.a1的地址减去ma的地址。

即   0x20000030 - 0x2000002c = 4;

结果出来了,a0变量占用了4个字节的内存,还有两个字节的内存空间去哪里了?直接屏蔽了呗。  

huahuagg|  楼主 | 2018-10-28 17:00 | 显示全部楼层
下面继续验证我的说法,编译器给结构体分配内存是,直接分配所含成员中,数据类型最大那个的占用字节数乘以元素个数。
997625bd57a3fa41c9.png
这张图片验证了,结构体可以分配1个字节,而结构体成员里面就一个char型变量,字节数最大占用为1。



看了这么多,下面来个题目。

struct a
{
char a0;
char b;

int a1;

}ma;

char a2,a3,a4;
int* a5;

int main()
{
a2 = sizeof(ma);
a3 = sizeof(char);
a4 = sizeof(int);
a5 = &ma.a1;
}



设ma地址0x2000002c,请问,a2、a3、a4、a5的值各为多少?
466265bd57a5bcb534.png

嘿嘿,是8,就是说,如果是连续的char型变量,编译器依旧会给它分配两个字节的内存空间。



把上题目改一下哈

struct a
{
char a0;

int a1;

char b;

}ma;

char a2,a3,a4;
int* a5;

int main()
{
a2 = sizeof(ma);
a3 = sizeof(char);
a4 = sizeof(int);
a5 = &ma.a1;
}



设ma地址为0x2000010c,请问,a2、a3、a4、a5的值各为多少?
786475bd57a7451127.png

这次的内存地址分配变更了,map文件如下

    a2                                       0x2000002c   Data           1  main.o(.data)
    a3                                       0x2000002d   Data           1  main.o(.data)
    a4                                       0x2000002e   Data           1  main.o(.data)
    a5                                       0x20000030   Data           4  main.o(.data)
    __stdout                                 0x20000038   Data           4  stdout.o(.data)
    Uart                                     0x2000003c   Data         208  main.o(.bss)
    ma                                       0x2000010c   Data          12  main.o(.bss)


从上面的仿真分析,ma.a0依旧是占用4个内存字节,ma.b也是4个。


为了使验证更使人信服,我增加了两个测试,增加位定义。



如图,即使我只声明了一个位,依旧分配了4个字节。
219925bd57aab15836.png

*.map文件中的地址分配如下

    ma                                       0x2000002c   Data           2  main.o(.data)
    a2                                       0x2000002e   Data           1  main.o(.data)
    a3                                       0x2000002f   Data           1  main.o(.data)
    a4                                       0x20000030   Data           1  main.o(.data)
    a5                                       0x20000034   Data           4  main.o(.data)


就是说,如果连续定义8个位,只占用了两个字节

综上所述,如果可能的话,在编写c语言的时候,尽量将数据类型相同的变量连续排在一起。

写完了,看懂了吗,不懂?我就喜欢你懵逼的样子,<( ̄︶ ̄)> 。


使用特权

评论回复
21mengnan| | 2018-10-28 21:55 | 显示全部楼层
按顺序分配

使用特权

评论回复
734774645| | 2018-10-29 07:53 | 显示全部楼层
对齐不对齐不清楚,能用就行。

使用特权

评论回复
598330983| | 2018-10-29 09:30 | 显示全部楼层
对齐是看数据长度

使用特权

评论回复
734774645| | 2018-10-29 15:20 | 显示全部楼层
现代计算机中,内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序一个接一个地存放,这就是对齐。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

112

主题

1163

帖子

1

粉丝