我们经常会遇到让我们计算某个结构体的所占字节这种问题,那么究竟怎么解决呢?
文章目录
1.什么是结构体对齐?为什么要进行结构体的对齐?
虽然说内存是连续的,但是我们在里面放入数据时,不是一个一个依次紧密相连得放进去,而是得按照一定的规律,变量之间可能存在着间隙得放在内存中,这就是结构体对齐;因为某些硬件平台只能从规定的地址处取某些特定类型的数据,因此进行对齐后能够更加方便读取数据。
2.如何计算结构体的字节数?
我们只需要按照两个规则来计算即可:
规则1:每个结构体成员在结构体中的相对字节必须是其基本数据类型所占字节的整数倍
相对字节是指某个成员在整个结构体中的相对位置,即第几个字节的位置。注意基本数据类型只有 int、char、bool 、double、float、void、floatwchar_t(宽字符型)7种,对于非基本数据类型的特殊的结构体成员,例如说数组,它在整个结构体中的相对字节必须其中一个元素所占字节的整数倍;若是结构体的成员也是一个结构体,首先需要单独计算出嵌套的结构体的字节大小,它的相对字节必须是其所有成员中最长基本数据类型的整数倍。
规则2:整个结构体大小必须是其所有成员中最长基本数据类型的整数倍。
EX1:普通结构体
struct A1{
int a;
double b;
char c[5];
bool d;
};//sizeof(A1)=24
按照规则1就是 a的相对字节必须是4的整数倍,我们取0,a就占了0-3这一部分内存,b的相对字节必须是8的整数倍,我们取8,b就占了8-15这部分内存,c的相对字节必须是(char)1的整数倍,我们取16,c就占了16-20这部分内存,d的相对字节取21即可;再按照规则2,整个结构体的字节数必须是8的整数倍,所以sizeof(A1)=24。
整个成员内存分布可以看下面这张图:
EX2:结构体中的成员的顺序发生改变
结构体A2改变了A1中的成员顺序,总体所占字节也发生改变。
struct A2{
bool d;
double b;
char c[5];
int a;
};//sizeof(A2)=32
整个结构体的成员内存分布如下图所示
EX3:当结构体嵌套结构体
struct B{
bool b;
double d;
};//sizeof(B)=16
struct A3{
int a;
B b1;
char c[3];
};//sizeof(A3)=32
比较方便计算出结构体B的总大小是16;对于结构体A, a的相对字节必须是4的整数倍,我们取0,a就占了0-3这一部分内存,把b1看做一个整体,所占字节是16,相对字节必须是double 的整数倍,因此b1就占了8-23这一部分内存,c相对字节是(char)1的整数倍,因此c占了24-26这部分内存;再根据规则2,整个结构体大小必须是double的整数倍,所以sizeof(A3)=32。
struct B:
struct A3:
3.结构体和联合体的区别
3.1为什么要有联合体union?
我们联合体用关键字union修饰,理解为“共用体”更加贴切,它在任何时刻只有一个成员存在,所有成员共享一段内存,每个成员的相对字节都是0,更加节约内存。
3.2 如何计算联合体的大小
联合体只要能容纳其中最宽的成员即可,同时满足是其中基本数据类型的整数倍。看下面的例子
union U1{
int a;
char c[5];
};//sizeof(U1)=8(大于等于5且是4的倍数)
union U2{
int a;
char c[3];
};//sizeof(U2)=4(3和4中4大)
union U3{
int a;
char c[3];
double d;
};//sizeof(U3)=8 (4,3,8中8最大)
3.3联合体赋值
那我们在对联合体赋值的时候,每个成员都会是什么情况呢?试了下面的小例子,只给联合体中的int型数据赋值,那么没有赋值的字符数组会是什么情况呢?
#include <iostream>
#include <string>
using namespace std;
union U1{
int a;
char c[5];
};
int main(){
U1 uu;
uu.a=4;
cout<<uu.a<<endl;
}
显然会输出4,那么如果是
cout<<uu.c[0]<<endl;
cout<<uu.c[1]<<endl;
cout<<uu.c[2]<<endl;
cout<<uu.c[3]<<endl;
cout<<uu.c[4]<<endl;
会分别输出什么呢?我在VScode里面调试,有结果但是显示不出来,觉得很奇怪,换了种方式显示:
if(uu.c[0]==4)cout<<"yes0"<<endl;//输出yes0
if(uu.c[1]==0)cout<<"yes1"<<endl;//输出yes1
if(uu.c[2]==0)cout<<"yes2"<<endl;//输出yes2
if(uu.c[3]==0)cout<<"yes3"<<endl;//输出yes3
if(uu.c[4]==0)cout<<"yes4"<<endl;//输出yes4
结果显而易见,都会输出,那么内存中的情况也比较确定了,虽然没有给字符数组赋值,但是访问它时是不会报错的,而是会默认从联合体的对应位置去访问它。
3.3利用联合体判断大小端
首先介绍一下大小端的概念.
大端模式,是指数据的高字节位保存在内存的低地址中,而数据的低字节位保存在内存的高地址中。
小端模式,是指数据的高字节位保存在内存的高地址中,而数据的低字节位保存在内存的低地址中.
#include <iostream>
#include <string>
using namespace std;
union U1{
int a;
char c[2];
};
int main(){
U1 uu;
uu.a=1;
if(&(uu.c[0])<&(uu.c[1]))cout<<"c[0]位于地地址,c[1]位于高地址。"<<endl;
if(uu.c[0]==1)cout<<"小端"<<endl;
else{
cout<<"大端"<<endl;
}
}
联合体的特点是所有成员相对地址都是0,上述代码首先将联合体uu的整型元素a赋值为1,因此内存中从高地址到低地址的比特数是00000000 00000000 00000000 00000001,后来又对c[0],c[1]赋值,首先判断出c[0]位于低地址,c[1]位于高地址,且c[0]对应的是00000001这部分内存,说明低地址的c[0]恰好是位于低字节的,因此是小段模式。
对应关系如图所示:由于字符变量c[0]=1,说明它对应的是内存的相对低字节,而c[1]=0,说明它对应的是内存的相对高字节;且c[0]位于低地址,c[1]位于高地址;而联合体的特性是所有的成员相对位置为0,因此是小端模式。