现在的位置: 首页 > 综合 > 正文

Linux 中结构体的内存分配问题

2014年01月18日 ⁄ 综合 ⁄ 共 7584字 ⁄ 字号 评论关闭

最近在学习Linux当中发现在使用结构体的时候会出现内存对齐问题,由于先前没有学习过关于内存分配对齐的知识,感觉很奇怪,今天终于在这里发现一篇文章讨论这个问题,有些了解了,原来是逐次对齐所致,然而如何解决,也没有看到有什么好办法,把大家的讨论请到这里!

 

刚刚试了试这样的结构

struct st
{
int i[4];
double b;
int a;
};

发现不论int i[4] 在任何位置,sizeof的结果都是32....和我以前看的不一样了.....

3q大家

 
  回复人:healer_kx(甘草{决心把CSDN改造成全国最大的文学爱好者的社区}) ( 两星(中级)) 信誉:98 2006-4-26 16:27:48 得分:0
 
 
?
为啥我觉得是28 啊?
Top
 
  回复人:cutenoob(cute ) ( 一级(初级)) 信誉:100 2006-4-26 16:34:15 得分:0
 
 
?
晕...你的一颗星哪来的?
Top
 
  回复人:dch4890164(跳梁小丑) ( 三级(初级)) 信誉:100 2006-4-26 16:43:22 得分:0
 
 
?
这个估计是和编译器有关
有数组和没有数组不一样
我刚才拿
struct st
{
int i[4];
double b;
int a;
};
struct a
{
int i;
int j;
double b;
};
st的确是32但是a是16说明有数组和没有数组存在区别,这有可能和数组的存储方式有关
要么数组是以存储一个地址 数组名4个 另外加上一个存储的元素4*4所以最后得32
不知道有人又更好的解释吗?
Top
 
  回复人:dch4890164(跳梁小丑) ( 三级(初级)) 信誉:100 2006-4-26 16:45:26 得分:0
 
 
?
呵呵
甘草被BS了
估计郁闷了
这两天不知道这些高手怎么了
麻烦你楼主,高手也不是神
尊重点,别动不动就晕。
Top
 
  回复人:sankt(做最好的自己) ( 一星(中级)) 信誉:110 2006-4-26 16:48:56 得分:0
 
 
?
struct st
{
int i[4];
double b;
int a;
};

发现不论int i[4] 在任何位置,sizeof的结果都是32....和我以前看的不一样了.....

//===========
以最大的double为对齐标准,就是8个字节
int i[4]; //16
double b; //8
int a; //8

所以 sizeof(st) == 32

Top
 
  回复人:dch4890164(跳梁小丑) ( 三级(初级)) 信誉:100 2006-4-26 16:54:32 得分:0
 
 
?
#pragma pack([ 4]);
楼上的改成这个结果还是一样
而且为什么
struct st
{
int i[4];
double b;
int a;
};
struct a
{
int i;
int j;
double b;
};
一个是32 一个是16如果不强行对齐的话。
呵呵
Top
 
  回复人:cutenoob(cute ) ( 一级(初级)) 信誉:100 2006-4-26 16:57:26 得分:0
 
 
?
﹃_﹃〣 刚才说的只是开玩笑了...

~~~~~~~~~~~~~~~~~~~~~~
sankt(黄景天)

以最大的double为对齐标准,就是8个字节
======

可是在这个结构中int i[4] 不是最大的么? 默认情况下不是按照最大的来对齐么....

Top
 
  回复人:sharpdew(风刃) ( 五级(中级)) 信誉:100 2006-4-26 17:06:39 得分:0
 
 
?
Windows下sankt的分析应该是没错的,至于有人说数组名也要占一个4字节,有点笑话。
Top
 
  回复人:sharpdew(风刃) ( 五级(中级)) 信誉:100 2006-4-26 17:08:33 得分:0
 
 
?
不是按照最大的来对齐,而是从头开始一步一步对齐,看看下面这个:
typedef struct ms3
{
char a;
short b;
double c;
} MS3;

内存正确的布局图如下:

padding
|
_____v_________________________________
| |/| |/////////| |
| a |/| b |/padding/| c |
| |/| |/////////| |
+-------------------------------------+
Bytes: 1 1 2 4 8

Top
 
  回复人:sharpdew(风刃) ( 五级(中级)) 信誉:100 2006-4-26 17:09:44 得分:0
 
 
?
上面图怎么乱了?
padding
|
_____v_________________________________
| |/| |/////////| |
| a |/| b |/padding/| c |
| |/| |/////////| |
+-------------------------------------+
Bytes: 1 1 2 4 8

Top
 
  回复人:healer_kx(甘草{决心把CSDN改造成全国最大的文学爱好者的社区}) ( 两星(中级)) 信誉:98 2006-4-26 17:20:24 得分:0
 
 
?
好了,我这颗星星来了。
#pragma pack(1)的时候就是28,
#pragma pack(4)的时候也是28
有啥问题嘛?

Top
 
  回复人:sharpdew(风刃) ( 五级(中级)) 信誉:100 2006-4-26 17:26:50 得分:0
 
 
?
甘草,计较这些干嘛,错了就错了咯,搞这些编译标记出来反而显得不大度,呵呵!

Top
 
  回复人:cutenoob(cute ) ( 一级(初级)) 信誉:100 2006-4-26 17:27:52 得分:0
 
 
?
我的问题是为什么不按照 i[4] 来对齐...

而且好像都是按照double来对齐的...在对齐中,i[4]到底在什么位置,编译器自动为它填充了多少?

Top
 
  回复人:sharpdew(风刃) ( 五级(中级)) 信誉:100 2006-4-26 17:36:09 得分:0
 
 
?
一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,被认为是未对齐的。这也就是说,char向其相邻的short对齐(当然是如果有相邻的short的话),short向相邻的int对齐,int向相邻的double进行对齐,当然这种对齐是从低地址往高地址对齐的。
一定不要以为是统一向最长的类型对齐,而是向2,4或者8这种边界依次对齐。
Top
 
  回复人:sharpdew(风刃) ( 五级(中级)) 信誉:100 2006-4-26 17:39:21 得分:0
 
 
?
因为i[4]占用两个8字节,而另外一个doube也是8字节,这样剩下的那个int因为只有4字节,所以怎么说都是这个int进行补齐,所以i[4]放到哪里都不影响结果,如果你换成i[5]就不同了!
Top
 
  回复人:sharpdew(风刃) ( 五级(中级)) 信誉:100 2006-4-26 17:53:48 得分:0
 
 
?
还有要说明的是,Linux下的GCC奉行的是:任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如long,double)都以4为对齐模数,从这里说 甘草 是没错的!
Top
 
  回复人:healer_kx(甘草{决心把CSDN改造成全国最大的文学爱好者的社区}) ( 两星(中级)) 信誉:98 2006-4-26 17:55:49 得分:0
 
 
?
你咋老打叹号呢? 看得我一惊一吒的。。。 。。。

Top
 
  回复人:sharpdew(风刃) ( 五级(中级)) 信誉:100 2006-4-26 18:05:11 得分:0
 
 
?
叹号就是强调,总比打问号要好,你以为呢?
Top
 
  回复人:cutenoob(cute ) ( 一级(初级)) 信誉:100 2006-4-26 18:06:35 得分:0
 
 
?
sharpdew(风刃)谢谢了
~~~~~~~

可否把我下面这个问题来讲下?

1
struct A
{
char c;
string str;
};
~~~~~~~
struct A
{
short int s;
string str;
};
~~~~~~~
struct A
{
int i;
string str;
};
~~~~~~~

以上的三个结构的sizeof都是20.....

Top
 
  回复人:sharpdew(风刃) ( 五级(中级)) 信誉:100 2006-4-26 18:22:59 得分:0
 
 
?
是str占用了16个字节,其他的char,short,int向4这个单位对其呀,简单的规则就是windows vs下是小于4向4对齐,大于4向8对齐,只是基本类型哦,如果是复杂对象的话展开其成员变量进行类似对其;linux下的gcc规定都向4对齐。
你最好再加一个:
struct A
{
double i;
string str;
};
这样就需要重新对齐了,编译系统具体的对齐方式相当于把string的成员展开到这个结构中进行从低位到高位的对齐。
Top
 
  回复人:ENOUGH_XU(苦点,累点->没关系) ( 二级(初级)) 信誉:100 2006-4-26 18:23:57 得分:0
 
 
?
就是一个内存分配上的问题吧.数据在内存中的分配起始地址总是从偶数地址起开始分配,所以分配的空间是大于等于28.即是内存对齐(书上都有)
Top
 
  回复人:dch4890164(跳梁小丑) ( 三级(初级)) 信誉:100 2006-4-26 18:30:51 得分:0
 
 
?
呵呵
看来关于内存对齐还得学.

Top
 
  回复人:leolovefun() ( 一级(初级)) 信誉:100 2006-4-26 23:07:44 得分:0
 
 
?
为什么不是16呢,不是刚刚好吗?
----------------------------
我是新手,请高人赐教
Top
 
  回复人:navi_dx() ( 二级(初级)) 信誉:100 2006-4-26 23:37:21 得分:0
 
 
?
学习了,我也已为就是28
Top
 
  回复人:sdbus(sdbus) ( 一级(初级)) 信誉:99 2006-4-26 23:57:43 得分:0
 
 
?
VC中默认8字节对齐,对于你的struct是int和double,因为8是4的倍数,所以跟位置无关了,如果你定义了一个short就有关了,
Top
 
  回复人:BaiYangSpirit(潜心学习技术) ( 二级(初级)) 信誉:100 2006-4-27 11:20:43 得分:0
 
 
?
VC中
sizeof(int) 是4
sizeof(double)是8;

int i[4],应该占16,刚好是两个sizeof(double),int i[4]和double b对齐了,所以不论你如何改变位置,也看不到你期望的结果
而另外一个int a;也占了8B。
===============
改成这样:
struct st
{
double b;
int i[3];
int a;
};
然后将int i[3];挪来挪去,就看到结果了
Top
 
  回复人:gjianpro(#ifndef _DEBUG) ( 二级(初级)) 信誉:100 2006-4-27 12:52:21 得分:0
 
 
?
eg:
struct B
{
char b;
int a;
short c;
};
假设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B共有12个字节,sizeof(struct B)=12;

Top
 
  回复人:yuanchuang(元创) ( 五级(中级)) 信誉:12 2006-4-27 13:13:52 得分:0
 
 
?
struct st
{
int i[5];
double b;
int a;
};//结果是40;
struct st
{
int i[6];
double b;
int a;
};//结果也是40
struct st
{
int i[4];
double b;
int a;
char c;
};结果32;
struct st
{
int i[5];
int a;
double b;
};//结果是32
……

上面的内容我随手写的,没有测试。
内存对其有很多细节的,包括下一个变量从什么位置开始,包括整个结构的内存补齐。
一言两语很难讲清

Top
 
  回复人:yuanchuang(元创) ( 五级(中级)) 信誉:12 2006-4-27 13:22:54 得分:0
 
 
?
有时间应该把内存对其整理一下了……
Top
 
  回复人:ugg(逸学堂(exuetang.net)) ( 两星(中级)) 信誉:98 2006-4-27 13:39:47 得分:0
 
 
?
struct st
{
int i[4]; // 16
double b; // 注意这时16字节是8的倍数,所以这里不用对齐,8
int a; // 前面是24是4倍数,所以是4
}; // 现在总共是28,因为28不是结构内,最大字节数8的倍数,调整为8的倍数即32。

再看下面
struct st
{
int i[5]; // 20
double b; // 因为20不是8的倍数,所以此时要补齐4个字节。空间大小为24+8
int a; // 32是4的倍数,大小为36
};// 结构调整,为40

以上说到的倍数都是这整数倍。

Top
 
  回复人:wjlsmail(小脖领) ( 一级(初级)) 信誉:100 2006-4-27 14:14:53 得分:0
 
 
?
struct st
{
int i[4]; // 4, 0 % 4 = 0, 4 * 5---- 这里是4个整型连续分布。这里可能涉及整个结构的平移
double b; // 8, 16 % 8 = 0, 8
int a; // 4, 24 % 4 = 0, 4
}; // max(4, 8, 4) = 8, (16 + 8 + 4) % 8 != 0 因此整个结构需要补齐,在后边补齐, 32

struct st
{
int i[5]; // 4, offset + 4 × 5
double b; // 8, 20 % 8 != 0, 因此向前补 4 个字节从 24开始分配 b 的内存, 8
int a; // 4, (20 + 4 + 8) % 4 = 0, 4
}; // max(4, 8, 4) = 8, (20 + 4 + 8 + 4) % 8 != 0, 整个结构需要补齐,在后边补齐, 40

Top
 
  回复人:wjlsmail(小脖领) ( 一级(初级)) 信誉:100 2006-4-27 14:25:06 得分:0
 
 
?
// 两个 32 是不一样的

struct st1
{
int a; // 4
double b; // 4 + 8
int i[4]; // 4 * 4
}; // max(4, 8, 4) = 8, 32 % 8 = 0, 32

struct st2
{
int i[4]; // 4 * 4
double b; // 8, 16 % 8 = 0, 8
int a; // 4, 24 % 4 = 0, 4
}; // max(4, 8, 4) = 8, 28 % 8 != 0, 后面补4个字节, 32

Top
 
  回复人:iawfnusr() ( 一级(初级)) 信誉:100 2006-4-27 16:21:34 得分:0
 
 
?
刚有人指点,
结构体的大小一定是结构体中的最大内置类型大小的倍数。
Top
 
  回复人:z331283538() ( 一级(初级)) 信誉:100 2006-4-27 17:07:05 得分:0
 
 
?
28
Top
 
  回复人:meicaikourou() ( 一级(初级)) 信誉:100 2006-4-27 17:19:45 得分:0
 
 
?
简单的规则是,在vc编译器下:内存对齐为最长类型的整数倍,不足补齐。
5*4(int) + 8(double) = 28,不是8的整数倍,补齐到32
Top
 
  回复人:cywater2000(如果) ( 二级(初级)) 信誉:100 2006-4-27 18:45:43 得分:0
 
 
?
mark
Top
 
  回复人:cattlenzq(吃狼的豆腐(不要给分了,散起来真麻烦!)) ( 三级(初级)) 信誉:100 2006-4-27 19:09:46 得分:0
 
 
?
因为32位寄存器一次最多读取的位数为32个2进制位,所以4字节对齐
Top
 
  回复人:hanyufeng(寒雨丰) ( 一级(初级)) 信誉:100 2006-4-27 19:19:52 得分:0
 
 
?
学习ing....
Top
 
  回复人:zqy2000zqy(ewrewrwe) ( 一级(初级)) 信誉:100 2006-4-30 11:27:37 得分:0
 
 
?
这个和编译器有关
linux下就不同
struct st
{
int i[5];
double b;
int a;
};//结果是32;
struct st
{
int i[6];
double b;
int a;
};//结果也是36
struct st
{
int i[4];
double b;
int a;
char c;
};结果32;
struct st
{
int i[5];
int a;
double b;
};//结果是32

Top
 
  回复人:zqy2000zqy(ewrewrwe) ( 一级(初级)) 信誉:100 2006-4-30 11:28:33 得分:0
 
 
?
linux最大照4对齐, 即使有double
Top
 
  回复人:xombat(壞牧羊人) ( 一级(初级)) 信誉:100 2006-4-30 11:52:26 得分:0
 
 
?
linux最大照4对齐, 即使有double
============================
study

Top
 
  回复人:ChoiceYi(简单生活) ( 一级(初级)) 信誉:100 2006-4-30 14:35:53 得分:0
 
 
?
meicaikourou()
简单的规则是,在vc编译器下:内存对齐为最长类型的整数倍,不足补齐。
5*4(int) + 8(double) = 28,不是8的整数倍,补齐到32
加上
gjianpro(#ifndef DEBUG) ( )
eg:
struct B
{
char b;
int a;
short c;
};
假设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B共有12个字节,sizeof(struct B)=12;
zqy2000zqy(ewrewrwe)
linux最大照4对齐, 即使有double

综合这几个答案应该是对这个问题有清晰的答案了

Top<

抱歉!评论已关闭.