C语言中union与struct的区别
在C语言中结构体和联合具有细微差别,特别是使用sizeof()对其求大小时,许多刚刚接触C语言不久的朋友对此非常困惑,下面我将简单谈一下自己对union与struct之间的区别 联 合(union)
1. 联合说明和联合变量定义
联合也是一种新的数据类型, 它是一种特殊形式的变量。
联合说明和联合变量定义与结构十分相似。其形式为:
union 联合名{
数据类型 成员名;
数据类型 成员名;
...
} 联合变量名;
联合表示几个变量共用一个内存位置, 在不同的时间保存不同的数据类型 和不同长度的变量。 union A
{
int a[5];
char b;
double c;
}Air;
表示声明了一个名称为A的联合,可以使用A variable 来定义联合变量。 在联合变量 variable中 整型和字符型以及double型共用同一内存位置。 当一个联合被说明时, 编译程序自动地产生一个变量, 其长度为联合中最大的变量长度。
联合访问其成员的方法与结构相同。同样联合变量也可以定义成数组或指针,但定义为指针时, 也要用"->"符号, 此时联合访问成员可表示成:
联合名->成员名
另外, 联合既可以出现在结构内, 它的成员也可以是结构。 union
{
int i;
struct
{
char first;
char second;
}half;
}number;
2.结构的定义
定义一个结构的一般形式为:
struct 结构名
{
成员表列
};
成员表由若干个成员组成, 每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为:
类型说明符 成员名;
成员名的命名应符合标识符的书写规定。例如:
struct stu
{
int num;
char name[20];
char >**;
float score;
};
在这个结构定义中,结构名为stu,该结构由4个成员组成。第一个成员为num,整型变量;第二个成员为name,字符数组;第三个成员为 >**,字符变量;第四个成员为score,实型变量。应注意在括号后的分号是不可少的。结构定义之后,即可进行变量说明。凡说明为结构stu的变量都由上述4个成员组成。由此可见, 结构是一种复杂的数据类型,是数目固定,类型不同的若干有序变量的集合。
在介绍了结构和联合的基本知识后,我们开始今天要讲的关于sizeof对结构体和联合求值问题:
让我们来看一道华为的面试题: 例:设有以下说明和定义: typedef union {long i; int k[5]; char c;} DATE;
struct data { int cat; DATE cow; double dog;} too;
DATE max;
则语句 printf( "%d",sizeof(struct date)+sizeof(max));的执行结果是:____ 需要说明的是typedef只是一个简单的名字替换,方便系统在不同的平台上移植,请参考以下例子: #include <stdio.h>
#include <stdlib.h>
int main()
{
typedef int Example;
Example a=10;//Equals to int a=10;
printf("%d\n",a);
system("pause");
}
解释:假设为32位机器,那么DATE是一个union, 变量公用空间. 里面最大的变量类型是int[5], 占用20个字节. 所以它的大小是20,data是一个struct,每个变量分开占用空间.依次为int4 + DATE20 + double8 = 32. 所以结果是 20 + 32 = 52. 再看下例: 设有以下说明和定义:
typedef union {double i; int k[5]; char c;} DATE;
struct data { int cat; DATE cow; double dog;} too;
DATE max;
则语句 printf( "%d",sizeof(struct date)+sizeof(max));的执行结果是:____
在草率回答这一问题之前我们先看一个小例子: #include <stdio.h>
#include <stdlib.h>
union A
{
int a[5];
char b;
double c;
};
int main()
{
printf("%d\n",sizeof(A));
system("pause");
}
对A用sizeof输出却得到的是24!union中变量共用内存,应以最长的为准,可是结果却不是我们预想的20,这是因为在联合内变量的默认内存对其方式,必须以最长的double8字节对齐,也就是说故应该是sizeof(A)=24;所以我们将联合中的int a[5] 修改成 int a[6] 结果仍然不变,但如果我们将int a[5]修改成 int a[7],结果就变成了 32了。 C语言sizeOf() 结构体对齐策略
struct AA
{
int num,*a;
char d;
double e;
};
int i1 = sizeof(int);//占用4个字节
int i2 = sizeof(int*);//占用4个字节
int i3 = sizeof(char);//占用1个字节
int i4 = sizeof(double);//占用8个字节
int i5 = sizeof(AA);//占用24字节 为什么结构体AA不是只用其成员的总和值4+4+1+8呢? 因为根据Win32平台下的微软 编译器(cl.exe for 80×86)的对齐策略: 1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。
备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。 是不是有点云里雾里呢?那让我们一起来遵循规律看下来吧 其中int i1 = sizeof(int)//偏移量为0 占用4个字节的长度 int i2 = sizeof(int*)//下一个偏移量为4(满足规则2)是i2=4 的整数倍 int i3 = sizeof(char);//下一个偏移量为8(满足规则2)是i=1 的整数倍 int i4 = sizeof(double);//下一个偏移量为9(不满足规则1)不能被 i4 = 8所整除,所以应当9+7 = 16 所以总数为16+i4(8)= 24(满足条件3) 所以为24 是否已经明白呢? 不凡再看以下代码 struct MyStruct
{
char dda; //偏移量为0,满足对齐方式,dda占用1个字节; double dda1; //下一个可用的地址的偏移量为1,不是sizeof(double)=8
//的倍数,需要补足7个字节才能使偏移量变为8(满足对齐
//方式),因此VC自动填充7个字节,dda1存放在偏移量为8
//的地址上,它占用8个字节。 int type; //下一个可用的地址的偏移量为16,是sizeof(int)=4的倍
//数,满足int的对齐方式,所以不需要VC自动填充,type存
//放在偏移量为16的地址上,它占用4个字节。 }; //所有成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构
//的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof
//(double)=8)的倍数,所以需要填充4个字节,以满足结构的大小为
//sizeof(double)=8的倍数。 其中要是不想遵循结构体对齐策略也可以用 #pragma pack 去掉内存对齐,去掉的话,应该会影响到效率吧。万一在嵌入式下的话,有可能导致程序出错。 |