联合体和结构体内存对齐分析

时间:2022-09-05 16:42:29

当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union)。在C Programming Language 一书中对于联合体是这么描述的:
1)联合体是一个结构;
2)它的所有成员相对于基地址的偏移量都为0;
3)此结构空间要大到足够容纳最”宽”的成员;
4)其对齐方式要适合其中所有的成员;
下面解释这四条描述:
由于联合体中的所有成员是共享一段内存的,因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0,即所有成员的首地址都是一样的。为了使得所有成员能够共享一段内存,因此该空间必须足够容纳这些成员中最宽的成员。对于这句“对齐方式要适合其中所有的成员”是指其必须符合所有成员的自身对齐方式。

下面举例说明:
如联合体

union U
{
    char s[9];
    int n;
    double d;
};

s占9字节,n占4字节,d占8字节,因此其至少需9字节的空间。然而其实际大小并不是9,用运算符sizeof测试其大小为16.这是因为这里存在字节对齐的问题,9既不能被4整除,也不能被8整除。因此补充字节到16,这样就符合所有成员的自身对齐了。从这里可以看出联合体所占的空间不仅取决于最宽成员,还跟所有成员有关系,即其大小必须满足两个条件:1)大小足够容纳最宽的成员;2)大小能被其包含的所有基本数据类型的大小所整除。

#include <stdio.h>
 2 
 3 union U1
 4 {
 5   char s[ 9 ];
 6   int n;
 7   double d;
 8 };
 9 
10 union U2
11 {
12   char s[ 5 ];
13   int n;
14   double d;
15 };
16 
17 int main( void )
18 {
19   union U1 u1;     /* 原来的代码此处有误 */
20   union U2 u2;     /* 原来的代码此处有误 */
21 
22   printf( "%d\n", sizeof( u1 ) );
23   printf( "%d\n", sizeof( u2 ) );
24 
25   printf( "0x%x\n", &u1 );
26   printf( "0x%x\n", u1.s );      /* &u1.s也正常工作…… 不是数组名就是地址么……*/
27   printf( "0x%x\n", &u1.n );
28   printf( "0x%x\n", &u1.d );
29 
30   /*
31   printf( "%d\n", u1.s[ 0 ] );
32   printf( "%lf\n", u1.d );
33   */
34 
35   u1.n = 1;
36   printf( "%d\n", u1.s[ 0 ] );
37   printf( "%lf\n", u1.d );
38 
39   unsigned char *p = ( unsigned char* )&u1;
40   printf( "%d\n", *p );
41   printf( "%d\n", *(p+1) );
42   printf( "%d\n", *(p+2) );
43   printf( "%d\n", *(p+3) );
44   printf( "%d\n", *(p+4) );
45   printf( "%d\n", *(p+5) );
46   printf( "%d\n", *(p+6) );
47   printf( "%d\n", *(p+7) );
48 
49   return 0;
50 }

输出结果为:

16
8
0x585d400
0x585d400
0x585d400
0x585d400
1
0.000000
1
0
0
0
0
0
0
0
对于sizeof(u1)=16。因为u1中s占9字节,n占4字节,d占8字节,因此至少需要9字节。其包含的基本数据类型为char,int,double分别占1,4,8字节,为了使u1所占空间的大小能被1,4,8整除,则需填充字节以到16,因此sizeof(u1)=16.
对于sizeof(u2)=8。因为u2中s占5字节,n占4字节,d占8字节,因此至少需要8字节。其包含的基本数据类型为char,int,double分别占1,4,8字节,为了使u2所占空间的大小能被1,4,8整除,不需填充字节,因为8本身就能满足要求。因此sizeof(u2)=8。
从打印出的每个成员的基地址可以看出,联合体中每个成员的基地址都相同,等于联合体变量的首地址。
对u1.n=1,将u1的n赋值为1后,则该段内存的前4个字节存储的数据为00000001 00000000 00000000 00000000
因此取s[0]的数据表示取第一个单元的数据,其整型值为1,所以打印出的结果为1.

转载自: https://www.cnblogs.com/nyc-emacs/archive/2012/07/20/2601789.html


联系方式: reyren179@gmail.com