如何计算结构体的内存大小

weiyf2008 2006-11-10 10:36:02
一个结构体,比如说下面的两个
struct a{int i; int j[10]; double k; float l; char w[5];};
struct b{double q; char e[4]; char *p; int r;};

象这样的计算怎么算?不用sizeof运算符
...全文
432 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
myfriend023 2006-11-10
  • 打赏
  • 举报
回复
用#pragma pack(n)时 系统取的是自己现在设置的和原编译器已经设置的两个的最小值
即如果设置的n比原来的编译器的小则设置生效 如果大则不生效 继续使用原来的对齐系数
我啃 2006-11-10
  • 打赏
  • 举报
回复
还是再转一篇简单的文给你看看吧

本来,一般是不自己计算sizeof的,知道内存对齐会对sizeof有影响,所以从来不手算,而是代码里写上sizeof。今天又看到http://blog.vckbase.com/smileonce/archive/2005/08/08/10658.html,翻来了http://blog.vckbase.com/billdavid/archive/2004/06/23/509.html ,自己想想还是也记录一下,万一以后自己真的也要计算sizeof,忘了,还能有个提示,也给不是很明白的朋友一个参考。
struct sample1
{
char a; /// sizeof(char) = 1
double b; /// sizeof(double) = 8
};
///default( 缺省#pragam pack(8) ——VC6和VC71,其它编译器,个人未知 )
///1+8 = 9 —> 16( 8 < 9 < 16 )

#pragma pack( 4 )
///1+8 = 9 —> 12( 8 < 9 < 12 )

#pragma pack( 2 )
///1+8 = 9 —> 10( 8 < 9 < 10 )

#pragma pack( 1 )
///1+8 = 9 —> 9

#pragma pack( 16 )
///1+8 = 9 —> 16( 16—>8 ---- 8 < 9 < 16 )

struct sample2
{
char a; ///1
int b; ///4
};
#pragma pack( 8 )
/// 1 + 4 = 5 —> 8( 8 —> 4 )

#pragma pack( 16 )
/// 1 + 4 = 5 —> 8( 16 —> 4 )

说明:#pragma pack告诉编译器进行内存边界对齐,一般都是采用编译器的设置对整个项目采用同一对齐方案,而且通常为缺省8字节对齐。



今天又看到以前测试的一段代码,突然不明白了起来,又稍写了几个测试。
struct sample3
{
char a; ///1
int b; ///4
char c; ///1
};
///default ///12
#pragma pack( 4 ) ///12
#pragma pack( 2 ) ///08
#pragma pack( 1 ) ///06
#pragma pack( 16 ) ///12

原来,其实编译器,根据对齐指示的对齐字节和最大成员的字节,对每个成员进行了对齐:编译器会取对齐指示和最大成员字节中较小的一个用于补齐其它成员。那么,上面的sample1/2/3也就都在情理之中了。为了证实这点,我们还再看一个例子:
struct sample4
{
char a; ///1
int b; ///4
double c; ///8
char d; ///1
};
///default: ///8+8+8+8 = 32
#pragma pack( 4 ) ///4+4+8+4 = 20
#pragma pack( 2 ) ///2+4+8+2 = 16
#pragma pack( 1 ) ///1+4+8+1 = 14
#pragma pack( 16 ) ///8+8+8+8 = 32
而实际上,编译器给出的值是:24、20、16、14、24
那么说明我错了。注意一下,我发现char a,int b加起来也才5<8,难到编译器进行了联合对齐?
struct sample5
{
char a; ///1
double c; ///8
int b; ///4
char d; ///1
};
编译器给出结果:24、20、16、14、24

这用联合对齐的解释正好符合,我又试验了不同的数据,发现这个结论并不太准确确。于是,我输出了每一个对象成员地址进行分析。由于试验数据量很大,这里就不列出了。

最后得到了以下结论:
1. 成员的对齐是按声明顺序进行的;
2. 对齐值由编译指示和最大成员两者较小的值决定;
3. 未对齐到对齐值的成员一起形成块对齐(联合对齐);
4. 上一个(下一个)对齐采用自己较大则不变,自己较小则填充自己对齐到上一个(下一个)大小;
5. 每成员对齐:如果前面已对齐到对齐值,下一个对齐自己。如果前面未对齐到对齐值,如果加上下一个成员不大于对齐值,下一个对齐自己,否则填充自己块对齐到对齐值。
6. 最后还未对齐到对齐值的,填充空间块对齐到对齐值。

从这些结论,可以得到:
1. 以上的对齐原则其实是尽量整齐排列、尽量节省内存。
2. 声明成员应该尽量避免不同类型错杂开来,最好采用从小到大或者从大到小的顺序(错开后,会因为上对齐和下对齐而增加填充开销)。
3. 编译器缺省采用8字节对齐主要是因为最大基本类型为8自己(以前自己不明白,在论坛提过问,后来,以为是SSE指令的原因)。
4. 手算sizeof是没有必要的,负责的(可以先对齐出对齐块,用块数乘对齐值)。

////////////////////////////////////////////////////////////////////////////////////////////////////////
在最近的项目中,我们涉及到了“内存对齐”技术。对于大部分程序员来说,“内存对齐”对他们来说都应该是“透明的”。“内存对齐”应该是编译器的“管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置上。但是C语言的一个特点就是太灵活,太强大,它允许你干预“内存对齐”。如果你想了解更加底层的秘密,“内存对齐”对你就不应该再透明了。

一、内存对齐的原因
大部分的参考资料都是如是说的:
1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

二、对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
我啃 2006-11-10
  • 打赏
  • 举报
回复
手动计算,根据编译器特性,一般sizeof(int)=4
char=1
long=4
short=2
double=8
数组算多个,同时要求注意内存对齐的问题,这个CSDN里已经不知道又多少文了
shootingsyh 2006-11-10
  • 打赏
  • 举报
回复
除非写底层代码,否则不要pack,移植性上有问题
lsm_1982 2006-11-10
  • 打赏
  • 举报
回复
包含位域的话,不可以跨过该类型的大小。
不同的类型,必须考虑对齐,该类型的倍数的内存出开始存放。
最后与最大的类型的对齐
taodm 2006-11-10
  • 打赏
  • 举报
回复
由于受对齐影响,楼主还是老老实实坚持用sizeof吧。
这个问题恐怕不是2、3天你能搞得很精通的。

64,647

社区成员

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

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