结构体与联合体

时间:2022-09-05 21:31:39


1. 定义一个结构体:

struct Student
{
int number; //学号
int score; //分数
struct Student *next; //一个指针类型的成员,既可以指向其他类型的结构体数据,也可以指向
//自己所在的结构体类型。
}

注意:定义结构体类型的时候 不可以对其进行赋值


2. 结构体字节大小问题:
字节对齐的细节和编译器实现相关,但一般而言,满足三个准则: 
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有
需要编译器会在成员之间加上填充字节(internal adding); 
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一
个成员之后加上填充字节(trailing padding)。 

2.1 struct Student
{
char a ;
int b;
} 8个字节。

2.2 struct Student
{
char a ;
int b ;
char c ;
}; 12个字节。


2.3 struct Student
{
char a ;
int b ;
char c ;
short d;
}; 12个字节。


2.4 struct Student
{
char a ;
short b;
char c;
}; 6个字节

2.5 struct Student
{
char a ;
int c;
short b;
}; 12个字节



结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。



联合体
                                                        联合体union
      当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(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)大小能被其包含的所有基本数据类型的大小所整除。
测试程序:
结构体与联合体
/*测试联合体 2011.10.3*/

#include <iostream>
using namespace std;

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

union U2
{
char s[ 5 ];
int n;
double d;
};

int main( int argc, char *argv[])
{
U1 u1;
U2 u2;
printf( "%d\n" , sizeof (u1));
printf( "%d\n" , sizeof (u2));
printf( "0x%x\n" ,&u1);
printf( "0x%x\n" ,&u1.s);
printf( "0x%x\n" ,&u1.n);
printf( "0x%x\n" ,&u1.d);
u1.n= 1 ;
printf( "%d\n" ,u1.s[ 0 ]);
printf( "%lf\n" ,u1.d);
unsigned char *p=(unsigned char *)&u1;
printf( "%d\n" ,*p);
printf( "%d\n" ,*(p+ 1 ));
printf( "%d\n" ,*(p+ 2 ));
printf( "%d\n" ,*(p+ 3 ));
printf( "%d\n" ,*(p+ 4 ));
printf( "%d\n" ,*(p+ 5 ));
printf( "%d\n" ,*(p+ 6 ));
printf( "%d\n" ,*(p+ 7 ));
return 0 ;
}
结构体与联合体

输出结果为:
16
8
0x22ff60
0x22ff60
0x22ff60
0x22ff60
1
0.000000
1
0
0
0
48
204
64
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.
至于打印出的d为0.000000愿意如下。由于已知该段内存前4字节的单元存储的数据为00000001 00000000 00000000 00000000,从上面打印结果48,204,64,0可以知道后面4个字节单元中的数据为00110000 11001100 01000000 00000000,因此其表示的二进 制浮点数为
00000000 01000000 11001100 00110000 00000000 00000000 00000000 00000001
对于double型数据,第63位0为符号位,62-52 00000000100为阶码,0000 11001100 00110000 00000000 00000000 00000000 00000001为尾数,根据其值知道尾数值约为0,而阶码为4-1023=-1019,因此其表示的浮点数为1.0*2^(-1019)=0.00000000000......,因此输出结果为0.000000。