以前用C语言时没多注意,似乎记得结构体的大小是其中各元素大小的简单相加。但今天,一位学弟在测试sizeof时发现了问题,问起来。经过测试发现确实不是如此,网上查资料,测试并总结如下。其实,还是自己没好好去研究C编译器的相关知识。
其实就是C语言里的字节对齐机制(C++里也是如此):
为满足以下原则,必要时编译器会填充字节。
一,结构体变量的首地址能够被其最宽基本类型的成员(其内嵌的结构体要展开来看,只看其中基本类型的成员变量)大小所整除。
二,结构体每个成员相对于结构体首地址的偏移量都是该成员大小的整数倍。
三,结构体总大小为结构体最宽基本类型成员大小的整数倍(即使有嵌套)。
四,结构体中的内嵌复合成员相对于结构体首地址的偏移量是该复合成员中最宽基本类型成员大小的整数倍。
共用体大小取决于占据最多内存的成员的长度。但同时要满足字节对齐机制(三),所以,其末尾必要时会填充字节。
下面是当时写的一个测试代码,可能并没考虑到所有的情况。
测试时用的是64位的编译器,64位编译器的基本类型中,大体与32位的相同。但指针在64位编译器中占8字节,32位编译器中占4个。
#include<cstdio>
struct A{ char a; }; //大小是1
struct B{ char a[3]; }; //..3
struct C{ char a[3]; short b; }; //..6
struct D{ char a[3]; int b; }; //..8
struct E{ char a[3]; short b; int c; }; //..12
struct F{ short a[3]; double b[4]; int c[5]; }; //..64
struct G{ char a[5]; double b; int c[7]; }; //..48
struct X{ char a[16]; };
struct H{ X a[5]; int b; }; //..84
struct Y{ double a[2]; };
struct I{ char a,b,c; Y d; }; //..24
struct J{ char a,b; double d; }; //..16
struct K{ char a; char b; double d; }; //..16
struct L{ char a; double d; char b; }; //..24
struct M{ char a, b[2], c; double d; }; //..16
struct N{ char a, *b, c; double d; }; //..32
struct O{ int *a[2]; double d; }; //..24
struct P{ double *a; int *b; double d; }; //..24
int main() {
printf( "A: %d\n", sizeof( A ) );
printf( "B: %d\n", sizeof( B ) );
printf( "C: %d\n", sizeof( C ) );
printf( "D: %d\n", sizeof( D ) );
printf( "E: %d\n", sizeof( E ) );
printf( "F: %d\n", sizeof( F ) );
printf( "G: %d\n", sizeof( G ) );
printf( "H: %d\n", sizeof( H ) );
printf( "I: %d\n", sizeof( I ) );
printf( "J: %d\n", sizeof( J ) );
printf( "K: %d\n", sizeof( K ) );
printf( "L: %d\n", sizeof( L ) );
printf( "M: %d\n", sizeof( M ) );
printf( "N: %d\n", sizeof( N ) );
printf( "O: %d\n", sizeof( O ) );
printf( "P: %d\n", sizeof( P ) );
}
其实就是C语言里的字节对齐机制(C++里也是如此):
为满足以下原则,必要时编译器会填充字节。
一,结构体变量的首地址能够被其最宽基本类型的成员(其内嵌的结构体要展开来看,只看其中基本类型的成员变量)大小所整除。
二,结构体每个成员相对于结构体首地址的偏移量都是该成员大小的整数倍。
三,结构体总大小为结构体最宽基本类型成员大小的整数倍(即使有嵌套)。
四,结构体中的内嵌复合成员相对于结构体首地址的偏移量是该复合成员中最宽基本类型成员大小的整数倍。
共用体大小取决于占据最多内存的成员的长度。但同时要满足字节对齐机制(三),所以,其末尾必要时会填充字节。
下面是当时写的一个测试代码,可能并没考虑到所有的情况。
测试时用的是64位的编译器,64位编译器的基本类型中,大体与32位的相同。但指针在64位编译器中占8字节,32位编译器中占4个。
#include<cstdio>
struct A{ char a; }; //大小是1
struct B{ char a[3]; }; //..3
struct C{ char a[3]; short b; }; //..6
struct D{ char a[3]; int b; }; //..8
struct E{ char a[3]; short b; int c; }; //..12
struct F{ short a[3]; double b[4]; int c[5]; }; //..64
struct G{ char a[5]; double b; int c[7]; }; //..48
struct X{ char a[16]; };
struct H{ X a[5]; int b; }; //..84
struct Y{ double a[2]; };
struct I{ char a,b,c; Y d; }; //..24
struct J{ char a,b; double d; }; //..16
struct K{ char a; char b; double d; }; //..16
struct L{ char a; double d; char b; }; //..24
struct M{ char a, b[2], c; double d; }; //..16
struct N{ char a, *b, c; double d; }; //..32
struct O{ int *a[2]; double d; }; //..24
struct P{ double *a; int *b; double d; }; //..24
int main() {
printf( "A: %d\n", sizeof( A ) );
printf( "B: %d\n", sizeof( B ) );
printf( "C: %d\n", sizeof( C ) );
printf( "D: %d\n", sizeof( D ) );
printf( "E: %d\n", sizeof( E ) );
printf( "F: %d\n", sizeof( F ) );
printf( "G: %d\n", sizeof( G ) );
printf( "H: %d\n", sizeof( H ) );
printf( "I: %d\n", sizeof( I ) );
printf( "J: %d\n", sizeof( J ) );
printf( "K: %d\n", sizeof( K ) );
printf( "L: %d\n", sizeof( L ) );
printf( "M: %d\n", sizeof( M ) );
printf( "N: %d\n", sizeof( N ) );
printf( "O: %d\n", sizeof( O ) );
printf( "P: %d\n", sizeof( P ) );
}
本文探讨了C语言中结构体大小不等于各成员简单相加的原因,实际上是由于字节对齐机制。结构体的大小遵循字节对齐规则,包括结构体变量首地址、成员相对偏移量和结构体总大小都需满足特定条件。通过测试不同结构体示例,展示了64位编译器下字节对齐的影响,例如结构体F、G、H等的大小并不只是成员的简单累加。

被折叠的 条评论
为什么被折叠?



