请教:关于内存对齐问题。请解释以下程序运行结果。
程序1:
#include <iostream>
using namespace std;
class B0 //声明基类B0
{ public: //外部接口
char nV;
char n;
//void fun(){cout<<"Member of B0"<<endl;}
};
class B1: virtual public B0 //B0为虚基类,派生B1类
{ public: //新增外部接口
int nV1;
};
class B2: virtual public B0 //B0为虚基类,派生B2类
{ public: //新增外部接口
int nV2;
};
class D1: public B1, public B2 //派生类D1声明
{
public: //新增外部接口
int nVd;
//void fund(){cout<<"Member of D1"<<endl;}
};
void main() //程序主函数
{
B0 b0;
B1 b1;
B2 b2;
D1 d1; //声明D1类对象d1
d1.nV=2; //使用最远基类成员
//d1.fun();
cout<<sizeof(B0)<<" "<<sizeof(B1)<<" "<<sizeof(B2)<<" "<<sizeof(D1)<<endl;
cout<<sizeof(b0)<<" "<<sizeof(b1)<<" "<<sizeof(b2)<<" "<<sizeof(d1);
}
运行结果:
2 10 10 22
2 10 10 22
程序2:
#include <iostream>
using namespace std;
class X1
{
int i;//4个字节
char c1;//1个字节
char c2;//1个字节
};
class X2
{
char c1;//1个字节
int i;//4个字节
char c2;//1个字节
};
class X3
{
char c1;//1个字节
char c2;//1个字节
int i;//4个字节
};
int main()
{
cout<<"long "<<sizeof(long)<<"\n";
cout<<"float "<<sizeof(float)<<"\n";
cout<<"int "<<sizeof(int)<<"\n";
cout<<"char "<<sizeof(char)<<"\n";
X1 x1;
X2 x2;
X3 x3;
cout<<"x1 的大小 "<<sizeof(x1)<<"\n";
cout<<"x2 的大小 "<<sizeof(x2)<<"\n";
cout<<"x3 的大小 "<<sizeof(x3)<<"\n";
return 0;
}
运行结果:
long 4
float 4
int 4
char 1
x1 的大小 8
x2 的大小 12
x3 的大小 8
在visual c++6.0中运行。
对于程序1,由于使用virtual继承,会增加“内部调整指针”,以免使子类重复拷贝同一个数据。由此B0:2个char,2个字节;B1:BO继承来的的2个char+内部调整指针(4字节)+自身int nV1(4字节)=10字节;同理B2也是10字节;D1:从B1和B2那里来的两个“内部调整指针”(8字节)+nV1(4字节)+nV2(4字节)+nVd(4字节)=20字节,其中函数由于存放在代码区,不参与类的sizeof计算。由此好像根本没有考虑内存对齐的问题。
对于程序2,显示考虑了内存对齐。而且好像是4字节对齐。
但是我在project->setting->c/c++->category/code generation->structure member alignment 里,无论怎么设置,结果都一样。
不知道我上面的分析有没有错。
到底什么时候对齐,什么时候不对齐,具体对齐是怎么个对齐法呢?迷惑中。。。。
欢迎大家发表意见。希望了解的高手们能够抽出点点时间帮我解除疑惑,感激不尽!
[解决办法]
所有的基本类型都有相应的对齐参数,编译器在编译时,会用全局的对齐参数和当前类型的对齐参数中较小的一个进行对齐.
比如,编译时你指定按8字节对齐,可以你的类型是char,由于char的对齐参数是1,所以最后还是按1字节对齐.
对于复合类型,所有的成员都按各自的对齐参数和全局对齐参数中较小的一处进行对齐.
比如:
struct {
char a;
short b;
long c;
}
这个结构里,a就对齐在1字节边界,b就对齐在2字节边界,c就对齐在4字节边界.最后这个结构就占8个字节...
对于这个结构来说,它的对齐参数则是它所有成员里最大的一个对齐参数,也就是说,这个结构在对齐的时候,就会按4字节对齐...
[解决办法]
还忘说了一点.
对于上面结构来说,对齐之后还要补齐,目的就是为了在设定这个类型的数组时,所有的项都可以正确的对齐到相应的位置.
就像你的例子里的
class X1
{
int i;//4个字节
char c1;//1个字节
char c2;//1个字节
};
虽然这个结构只占了6个字节,但由于它的成员i是4字节对齐的,也就要求结构按4字节对齐,如果它sizeof是6的话,就会导致定义数组时,第二项无法对齐.所以这时编译器会给结构补两个字节.以保证结构的大小是它对齐参数的整数倍.
[解决办法]
ls说的好。
对于第一个因为都是char,所以就不需要补齐了,只需要对齐就可以,所以为2,
两个10增加的是一个int和一个指针(指向vtbl)(从你的结果猜测是这样)
后面的22,因为是virtual继承,原来的两个char不变,分别增加了B1 B2 比B多的那部分就是 2+(12-2)+(12-2),可以搜索virtual 继承或者“菱形继承”
[解决办法]
综上所述:
Win32平台下的微软 编译器(cl.exe for 80×86)的对齐策略:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。
备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。
[解决办法]
UP
[解决办法]
up
记得c++必知必会中说过,最好保持对类实现的良好的不知情;否则用POD风格
MARK
[解决办法]
c/c++内存对齐
http://blog.csdn.net/sharpdew/articles/772504.aspx
[解决办法]
引用楼主:对于程序2,显示考虑了内存对齐。而且好像是4字节对齐。
但是我在project->setting->c/c++->category/code generation->structure member alignment 里,无论怎么设置,结果都一样
PS: 你加个在主函数前面 #pragma pack(1)
试试
[解决办法]
还有一种对齐方式,那就是强制对齐。
强制对齐与自然对齐取小的值,就是实际对齐的方式。
当确定实际对齐方式以后,如果在前一个成员剩余的对齐空间不足以放下该成员时,则该成员放到下一个存储空间。
[解决办法]
mark,不错的一个论题
[解决办法]
4楼说得不错,支持
[解决办法]
程序1:
b0:| char1 | char1 | == 2
b1,b2:| vptr4 | int4 | char1 | char1 | = 10
d1:| vptr(b1)4 | int(b1)4 | vptr(b2)4 | int(b2)4 | int4 | char1 | char1 | = 22
最后的两个char型,编译器是不给予补齐的
程序2:
x1:| int4 | char1 | char1 | 补2 | = 8
x2:| char1 | 对齐3 | int4 | char1 | 补3 | = 12
x3:| char1 | char2 | 对齐2 | int4 | = 8
程序1改:
b0:| char1 | 对齐7 | double8 | = 16
b1:| vptr4 | char1 | 对齐3 | int4 | 对齐4 | (b0)16 = 32 //最后的B0是8字节一块的,所以B0的首地址要对齐到8字节的边缘
b2:| vptr4 | int4 | (b0)16 | = 24 //先前的4+4=8已经让B0首地址对齐到8字节边缘了,所以不用添加对齐字节
d1:| (b1)32-(b0)16 = 16 | (b2)24-(b0)16 = 8 | int4 | 对齐4 | (b0)16 = 48 //前两部分是b1和b2扣掉各自后面的b0,然后跟上d1自己的数
据,这样总共是16+8+4=28,不在8的整数倍,所以补4,等于32,再把b0放上,就是48
[解决办法]
我想知道这是物理内存还是c语言中的内存
[解决办法]