一、结构体
1.结构体的作用
在网络协议、通信控制、嵌入式系统的C/C++编程中,我们经常要传送的不是简单的字节流,而是多种不同类型的数据组合起来的一个整体,其表现形式就是一个结构体。
最近在做项目,做的是智能家居的项目,APP模块和服务器之间通信,它们双方之间通信通过TCP协议通信,相互发送数据包,数据包的格式如下:
如上图数据包格式,APP模块与服务器之间数据包传送只通过字节流,显然是不合理的,双方只收到一串字符串,解析起来很麻烦;通过构建结构体来构建数据包,解析就简单很多了,构建结构体的形式如下:
struct dataPackage
{
unsigned char messageType;//报文类型:1字节
unsigned char functionNum;//功能号:1字节
unsigned short dataLenth;//数据长度:2字节
char strJson[1];//数据内容:长度为1的char类型数组
};
大家看到数据内容我只定义长度为1的char类型数组,都会说数据内容怎么可能只用长度为1的char型数组表示?
这是学习Android底层源代码时学习到的一种表示方法,谷歌的程序员果真是“老司机”,这样就可以根据需要去分配多大内存,不会由于定义过长的char数组造成分配多余的内存,浪费内存,这种表示方法也被称为
变长结构体;变长结构体,大家应该可以见名之意,比如我们现在作为APP模块需要发送数据包给服务器,我们需要做的就是分配结构体的大小并用数据按要求填充dataPackage结构体,假设我们要发送(0xaa 0x00 0x05 "abcde")这个数据包,结构体分配代码如下:
struct dataPackage *dataPack = new struct dataPackage[sizeof(struct dataPackage) + strlen("abcde")];
虽然这样分配内存大小,访问strJson数组会出现内存越界的问题,但是没关系,访问的是我们自己手动分配内存的内容,虽然越界,但是合法也合理。
2.结构体的大小
我们都知道运算符sizeof可以计算出给定类型的大小,对于32位系统来说,sizeof(char)=1;sizeof(int)=4。
基本数据类型的大小很好计算,如何计算构造数据类型的大小?
C语言中的构造数据类型有三种:数组、结构体和共用体。
数组是相同类型数据的元素的集合,只要会计算单个元素的大小,整个数组所占空间等于基础元素大小乘上元素的个数。
结构体的成员可以是不同的数据类型,成员按照定义时的顺序依次存储在连续的内存空间。和数组不一样的是,结构体的大小不是所有成员大小那么简单的相加,需要考虑到系统在存储结构体变量时的地址对齐问题。
与地址对齐相关联的还有一个很重要的概念就是偏移量。
偏移量指的是结构体中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。
下面来看一下一个简单的结构体,让大家加深偏移量的概念:
struct stu
{
int i;
char c;
int j;
};
大家都知道结构体变量中的第一个成员的地址就是结构体变量的首地址。所以第一个成员i的偏移量为0;第二个成员c的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为0;第三个成员j的偏移量是第二个成员的偏移量加上第二个成员的大小(4+1),其值为5。
偏移量总结:偏移量是结构体变量中成员的地址和结构体变量地址的差,通俗地说就是:
当前结构体变量成员的偏移量 = 前一个结构体变量成员的偏移量 + 前一个结构体变量成员的大小
实际上,由于存储变量时地址对齐的要求,编译器在编译程序时会遵循两条原则:
①当前结构体变量中成员的偏移量必须是当前成员大小的整数倍(0被认为是任何数的整数倍)
②结构体大小必须是所有成员大小的整数倍
对照第一条,上面的例子中前两个成员的偏移量都满足要求,但第三个成员的偏移量是5,并不是自身(int)大小的整数倍。编译器在处理时会在第二个成员后面补上3个空字节,使得第三个成员的偏移量变成8。
对照第二条,结构体大小等于最后一个成员的偏移量加上其大小,可算可知结构体大小为12,12是所有成员大小的整数倍,满足要求。
再看一个满足第一条,不满足第二条的例子:
struct stu1
{
int k;
short t;
};
成员k的偏移量为0,;成员t的偏移量为4,偏移量满足第一个要求,不需要调整。但是计算出来的大小为6,显然不是成员k大小的整数倍。因此,编译器会在成员t后面补上2个字节,使得结构体的大小变成8,从而满足第二个要求。
由此可见,大家在定义结构体类型时需要考虑到字节对齐的情况,不同的顺序会影响到结构体的大小。
struct stu3
{
char c1;
int i;
char c2;
};
struct stu4
{
char c1;
char c2;
int i;
};
虽然结构体stu3和stu4中成员都一样,但是sizeof(struct stu3)的值为12,而sizeof(struct stu4)的值为8。
还有一种情况就是结构体中又是另外一种结构体类型时应该怎么计算?如:
struct temp
{
char a[5];
struct
{
int w;
double e;
int h;
}ss;
double d;
}tem;
只需把其中的结构体展开即可,但需注意,
展开后的结构体的第一个成员(int w)偏移量应当是被展开的结构体(ss所属的结构体)中最大的成员(double)的整数倍。
所以上面结构体大小为40,读者根据前面的计算方法便可算出。
注意:在Linux下,char对齐模数是1,short是2,int是4,float是4,double(Linux是4,Windows是8)。
所以对于如下的结构体:(32位机,默认设置)
struct temp
{
int i;
double j;
};
对于Windows系统下,整个结构体的大小应该是16,在Linux系统下,整个结构体大小应该是12。
阅读(1292) | 评论(0) | 转发(0) |