疑问,关于内存对齐操作.....>_<

cutenoob 2006-04-26 04:01:32
刚刚试了试这样的结构

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

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

3q大家
...全文
916 51 打赏 收藏 转发到动态 举报
写回复
用AI写文章
51 条回复
切换为时间正序
请发表友善的回复…
发表回复
tb01412 2006-05-02
  • 打赏
  • 举报
回复
不要用sizeof(struct gg),这个不能说明问题,你需要在定义一个gg的变量后,再在其后定义另一个变量,将这两个变量的地址相减才能求出实际结果,而且你的测试一定是在VC中,GCC中是4个字节的

不能用这种方式来求结构体占用的空间,这是不准确的,不过在另一种程度上可以说得过去,因为结构体地址是被4整除,而结构体的第一个成员所在地址与结构体本身所占地址之间没有空洞存在的,而其成员又只占一个字节,所以VC编译器的sizeof语句就把它设成了一个字节,这跟编译器密切相关,你换成GCC之后就不一样了,无论这个表达示的结果如何,其内存占用空间的布局是可以预测的,只要知道了这个道理,就可以举一反三了,所以那些面试题出的sizeof之类的简直没有任何意义,不去追求本质,去追求现象有什么用呢???
之所以在VC下的sizeof设成这种规则,其目的是让人更好理解,屏蔽了细节,让人知其然不知其所以然,细想一下:什么时候会用sizeof,无非就是填充数据的时候,如果你需要动态地确定该填多少字节,比较好的方法就是先用sizeof这个表达示求出来,在
struct gg
{
char a;
}
中,写代码的人一看就知道只有一个字节被占用,所以理所当然就只填充一个字节进去,余下的工作就交给编译器去做,因为编译器会自动填充余下的三个空洞,而但是sizeof(struct gg)=1让人看起来更自然,因为其内容就只有一个字节啊,当然结果就应该是1个字节了啊,无论你将sizeof表达示的值置成1或者4,对程序结果没有任何影响,用下面的例子来说明:
struct gg test;
memset(&test,0,sizeof(struct gg));
如果sizeof(struct gg)结果是1,那么内存中接下来的三个字节就是编译器填充的固定字符,这个字符在GCC下就是0xc, 也就是说内存中第一个字节是0,接下来的三个字节是0xc,如果sizeof(struct gg)结果是4,那么内存中的四个字节都是0,说到这里,好像结果造成了明显的不同之处,但实际没有什么影响,接下来,如果调用下面一条语句:
test.a='a';
那么编译器会帮你做一个工作,你将其反汇编看一下就明白了,就是将四个字节全部取出,但会忽略掉三个字节,也就是让那三个字节形同虚设,而只取那个a所占8位的数据,并修改其数据,其余24位不变,最后再存回内存,所以无论你的sizeof(struct gg)是1或是4,对你的代码是透明的!!!!!而微软的东东一向就是以易用为原则,为了让程序员更好理解,所以就让sizeof(struct gg)等于1,而不是4,随之而来的就会给想要了解其细节的人设了一道障碍,而GCC刚好相反,处处在显示其实现细节,而不是隐藏细节

最后还想多说两句:现在很多公司的面试题都考这个内容,其实这些人出题根本不严谨,没有说是什么CPU,什么平台,什么编译器,不同的环境下其结果是不同的,而且根本就没考出重点,重点是在理解内存布局,是在编译器的处理,而不是一个表达示,这个表达示,死背都背得出来,但很少人能说出为什么!!!!!
你可以写下下面的测试代码,观测其执行结果,再回头来思考:
#include <stdio.h>
struct gg
{
char a;
}

stuct gg test;
char test_str_temp1[4];
char test_str_temp2[4];

int main(int argc,char **argv)
{
memcpy(test_str_temp1,&test,4);
test.a = 'a';
memcpy(test_str_temp2,&test,4);
printf("*******0x%x 0x%x 0x%x 0x%x *******\n",test_str_temp1[0],test_str_temp1[1],test_str_temp1[2],test_str_temp1[3]);
printf("*******0x%x 0x%x 0x%x 0x%x *******\n",test_str_temp2[0],test_str_temp2[1],test_str_temp2[2],test_str_temp2[3]);
printf("------0x%x---------\n",test.a);
return 0;
}
leolovefun 2006-05-02
  • 打赏
  • 举报
回复
結構的大小為結構中最長成員大小的倍數( 2(short)、4(int)、8(double) ),如不想等就自動添加 bit 湊齊,剛剛看到其他的問題回覆學到D.不知道對不對???
tb01412 2006-05-02
  • 打赏
  • 举报
回复
用下面的程序更好说明这个问题的:
#include <stdio.h>

char test_str_temp1[4];
char test_str_temp2[4];
char test_str_temp3[4];
int main(int argc,char **argv)
{
struct gg
{
char a;
};

struct gg test;
int empty;
empty = 1;
memcpy(test_str_temp1,&test,4);
test.a = 'a';
memcpy(test_str_temp2,&test,4);
memcpy(test_str_temp3,&empty,4);
printf("*******0x%x %d %d %d *******\n",test_str_temp1[0],(int)test_str_temp1[1],(int)test_str_temp1[2],(int)test_str_temp1[3]);
printf("*******0x%x %d %d %d *******\n",test_str_temp2[0],(int)test_str_temp2[1],(int)test_str_temp2[2],(int)test_str_temp2[3]);
printf("------0x%x---------\n",test.a);
printf("*******0x%x %d %d %d *******\n",test_str_temp3[0],(int)test_str_temp3[1],(int)test_str_temp3[2],(int)test_str_temp3[3]);

return 0;
}
tb01412 2006-05-01
  • 打赏
  • 举报
回复
to 楼上的:
我在上一回贴中已纠正我的错误,就是double类型变量的自然对齐方式依然是被4整除,而不是被8整除,也就是说既然结构体本身能被4整除,它的第一个成员是double型,double型的自然对齐方式同样是被4整除,所以这个成员与结构体本身所在地址之间没有空洞的!!!!!!我之前没有想到double型的自然对齐方式是被4整除,而理解成了被8整除,这是我犯的错误,不好意思!!!!

其实一种类型的自然对齐方式并不一定是这个成员本身所占内存大小,LINUX下的GCC所支持的各种类型自然对齐方式,其最大为被4整除的,比如一个结构体无论它本身占用多大空间,其自身所在地址依然是被4整除的,而不是结构体有多大,它所处的地址就以多大的字节对齐!!!这是一个误区,之所以最大以4字节对齐,是由于CPU寻址决定的,CPU的寻址就是4字节,所以你用4字节对齐与8字节对齐对于CPU来说没有任何影响,既然没有影响,那么为何还要去留下空洞呢???

除了上面我说的那个错误之外,其它的都是正确的,可以去理解一下!!!!这样就可以举一反三了!!!
小春222 2006-05-01
  • 打赏
  • 举报
回复
to 楼上的。

你的意思我明白,你开始说的有些让人费解, 因为讨论的是sizeof() 这个表达式
比如struct gg
{
char a;
}
这个结构体的变量确实实际占用的4个字节,因为cpu按字寻址,
但是sizeof(struct gg)=1; !!!
xombat 2006-04-30
  • 打赏
  • 举报
回复
linux最大照4对齐, 即使有double
============================
study
小春222 2006-04-30
  • 打赏
  • 举报
回复
LINUX下的GCC对结构体对齐默认情况下用以下的标准:
1.保证结构体变量本身的地址一定是4的整倍数
2.保证结体中任何变量所处地址按其自身的自然对齐方式,比如:
一个结构体的一第个成员就是double型,那么GCC就一定保证这个成员所处地址一定被8整除,由于结构体本身的地址是按4字节对齐的,那么这个结构体的开始处就有4字节的空洞存在!!!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
如果结构体的第一个成员是short型,第二个成员是int型,那么由于结构体本身地址是能被4整除,那么这个地址就当然可以被2整除,所以第一个成员的地址就是结构体变量的首地址,其间没有空洞,再往下看,第二个成员是int型,GCC一定要保证其被4整除,由于结构体首地址能被4整除,再加一个short型变量所占空间之后就不能再被4整除,所以GCC就会在第二个成员与第一个成员间填充两个字节的空洞,从而保证第二个成员,也就是int型的那个变量一定要能被4整除的
以此类推,再回过去看上面的回贴中的所有例子,你就明白对齐是怎么回事了,需要注意一点的是:对齐的处理与编译器密切相关,所以我不能保证这个东东能在非GCC编译器下通过
=====================================================================

struct x
{
double a;
};
你的意思 这个sizeof(struct x) 是8+4 ???
小春222 2006-04-30
  • 打赏
  • 举报
回复
linux最大照4对齐, 即使有double
小春222 2006-04-30
  • 打赏
  • 举报
回复
这个和编译器有关
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
tb01412 2006-04-30
  • 打赏
  • 举报
回复
不好意思,纠正一下我上一回贴中的错误,LINUX下GCC在对double类型的成员时,其地址同样是被4整除的,而不是被8整除
lei001 2006-04-30
  • 打赏
  • 举报
回复
四字节对齐的,无论i[4]在什么位置,大小不会变!
试试下面的情况,应该是不一样的
struct{
short a;
int i;
short b
}Node;
tb01412 2006-04-30
  • 打赏
  • 举报
回复
linux最大照4对齐, 即使有double
============================

你好像错了!!!!

LINUX下的GCC对结构体对齐默认情况下用以下的标准:
1.保证结构体变量本身的地址一定是4的整倍数
2.保证结体中任何变量所处地址按其自身的自然对齐方式,比如:
一个结构体的一第个成员就是double型,那么GCC就一定保证这个成员所处地址一定被8整除,由于结构体本身的地址是按4字节对齐的,那么这个结构体的开始处就有4字节的空洞存在!!!
如果结构体的第一个成员是short型,第二个成员是int型,那么由于结构体本身地址是能被4整除,那么这个地址就当然可以被2整除,所以第一个成员的地址就是结构体变量的首地址,其间没有空洞,再往下看,第二个成员是int型,GCC一定要保证其被4整除,由于结构体首地址能被4整除,再加一个short型变量所占空间之后就不能再被4整除,所以GCC就会在第二个成员与第一个成员间填充两个字节的空洞,从而保证第二个成员,也就是int型的那个变量一定要能被4整除的
以此类推,再回过去看上面的回贴中的所有例子,你就明白对齐是怎么回事了,需要注意一点的是:对齐的处理与编译器密切相关,所以我不能保证这个东东能在非GCC编译器下通过
ChoiceYi 2006-04-30
  • 打赏
  • 举报
回复
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

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






gjianpro 2006-04-27
  • 打赏
  • 举报
回复
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;
BaiYangSpirit 2006-04-27
  • 打赏
  • 举报
回复
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];挪来挪去,就看到结果了
hanyufeng 2006-04-27
  • 打赏
  • 举报
回复
学习ing....
吃狼的豆腐 2006-04-27
  • 打赏
  • 举报
回复
因为32位寄存器一次最多读取的位数为32个2进制位,所以4字节对齐
cywater2000 2006-04-27
  • 打赏
  • 举报
回复
mark
meicaikourou 2006-04-27
  • 打赏
  • 举报
回复
简单的规则是,在vc编译器下:内存对齐为最长类型的整数倍,不足补齐。
5*4(int) + 8(double) = 28,不是8的整数倍,补齐到32
z331283538 2006-04-27
  • 打赏
  • 举报
回复
28
加载更多回复(31)

65,210

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧