为什么sizeof运算符返回的结构大小大于结构成员的总大小?
当前回答
另请参见:
对于Microsoft Visual C:
http://msdn.microsoft.com/en-us/library/2e70t5y1%28v=vs.80%29.aspx
GCC声称与微软编译器兼容
https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Structure_002dPacking-Pragmas.html
除了前面的答案,请注意,无论包装如何,C++中没有成员订单保证。编译器可以(当然也可以)向结构中添加虚拟表指针和基结构的成员。即使是虚拟表的存在也没有得到标准的保证(没有规定虚拟机制的实现),因此可以得出这样的保证是不可能的。
我很确定C语言保证了成员顺序,但在编写跨平台或跨编译器程序时,我不会指望它。
其他回答
例如,如果您希望结构具有GCC的特定大小,请使用__attribute__((打包))。
在Windows上,使用带有/Zp选项的cl.exe编译器时,可以将对齐设置为一个字节。
通常,CPU更容易访问4(或8)的倍数的数据,这取决于平台和编译器。
所以这基本上是一个对齐问题。
你需要有充分的理由来改变它。
其思想是,出于速度和缓存的考虑,操作数应从与其自然大小对齐的地址中读取。为了实现这一点,编译器填充结构成员,以便对齐以下成员或以下结构。
struct pixel {
unsigned char red; // 0
unsigned char green; // 1
unsigned int alpha; // 4 (gotta skip to an aligned offset)
unsigned char blue; // 8 (then skip 9 10 11)
};
// next offset: 12
x86体系结构始终能够获取未对齐的地址。然而,它速度较慢,当未对齐与两个不同的缓存线重叠时,当对齐的访问只会逐出一个缓存线时,它会逐出两个缓存线。
有些架构实际上必须捕获未对齐的读写,而早期版本的ARM架构(演变成当今所有移动CPU的架构)。。。事实上,他们只是返回了这些错误的数据。(他们忽略了低位。)
最后,请注意缓存线可以任意大,编译器不会试图猜测这些缓存线,也不会做出空间与速度的权衡。相反,对齐决策是ABI的一部分,表示最终将均匀填充缓存行的最小对齐。
TL;DR:对齐很重要。
打包和字节对齐,如C常见问题解答中所述:
这是为了对齐。许多处理器无法访问2字节和4字节数量(例如整数和长整数),如果它们被塞进每个方向。假设您有这样的结构:结构{字符a[3];短整数b;长整型c;字符d[3];};现在,你可能认为应该可以打包这个结构如下:+-------+-------+-------+-------+|a | b|+-------+-------+-------+-------+|b | c|+-------+-------+-------+-------+|c | d(c | d)|+-------+-------+-------+-------+但如果编译器安排,则处理器上的操作要简单得多它是这样的:+-------+-------+-------+|一个|+-------+-------+-------+|b级|+-------+-------+-------+-------+|c类|+-------+-------+-------+-------+|d)|+-------+-------+-------+在打包版本中,请注意,对于你和我想看看b和c字段是如何换行的?简而言之处理器也很难。因此,大多数编译器都会填充结构(好像有额外的、不可见的字段)如下:+-------+-------+-------+-------+|a |焊盘1|+-------+-------+-------+-------+|b |焊盘2|+-------+-------+-------+-------+|c类|+-------+-------+-------+-------+|d|焊盘3|+-------+-------+-------+-------+
在关于内存对齐和结构填充/打包的其他解释得很好的答案中,我通过仔细阅读问题本身发现了一些东西。
“为什么结构的sizeof不等于每个成员的sizeof之和?”“为什么sizeof运算符返回的结构大小大于结构成员的总大小”?
这两个问题都表明了一些明显的错误。至少在一般的、非示例性的视图中是这样的。
应用于结构对象的sizeof操作数的结果可以等于分别应用于每个成员的sizeof之和。它不一定要更大/不同。
如果没有填充的原因,则不会填充内存。
如果结构仅包含相同类型的成员,则大多数实现为:
struct foo {
int a;
int b;
int c;
} bar;
假设sizeof(int)==4,结构杆的尺寸将等于所有构件的尺寸总和,sizeof(bar)==12。这里没有填充。
同样的例子如下:
struct foo {
short int a;
short int b;
int c;
} bar;
假设sizeof(short int)==2,sizeof(int)==4。为a和b分配的字节之和等于为c分配的字节,c是最大的成员,因此所有内容都完全对齐。因此,sizeof(bar)==8。
这也是关于结构填充的第二个最受欢迎的问题的对象,这里:
C结构中的内存对齐
这可能是由于字节对齐和填充,使得结构在您的平台上达到偶数字节(或单词)。例如,在Linux上的C中,以下3种结构:
#include "stdio.h"
struct oneInt {
int x;
};
struct twoInts {
int x;
int y;
};
struct someBits {
int x:2;
int y:6;
};
int main (int argc, char** argv) {
printf("oneInt=%zu\n",sizeof(struct oneInt));
printf("twoInts=%zu\n",sizeof(struct twoInts));
printf("someBits=%zu\n",sizeof(struct someBits));
return 0;
}
成员的大小(以字节为单位)分别为4字节(32位)、8字节(2x 32位)和1字节(2+6位)。上面的程序(在使用gcc的Linux上)将大小打印为4、8和4,其中最后一个结构填充为一个单词(在我的32位平台上为4 x 8位字节)。
oneInt=4
twoInts=8
someBits=4
推荐文章
- cplusplus.com给出的错误、误解或坏建议是什么?
- C多行宏:do/while(0) vs作用域块
- 找出质数最快的算法是什么?
- time_t最终的类型定义是什么?
- 我需要显式处理负数或零时,总和平方数字?
- c++枚举类可以有方法吗?
- 函数名周围的括号是什么意思?
- 用C语言创建自己的头文件
- 格式化IO函数(*printf / *scanf)中的转换说明符%i和%d之间的区别是什么?
- 将析构函数设为私有有什么用?
- main()中的Return语句vs exit()
- 为什么c#不提供c++风格的'friend'关键字?
- 如果不是内存地址,C指针到底是什么?
- 在函数的签名中添加关键字
- 我如何在Visual Studio中预处理后看到C/ c++源文件?