一、结构体大小
首先我们先定义一个非空结构体,通过sizeof()命令观察其内存字节大小,发现一个结构体的大小并不是结构体内各数据类型大小简单的相加。那么56究竟是怎么来的呢?这就引入了结构体对齐方式的问题。
#include<stdio.h>
struct Stu{
char name[20];//20
unsigned int age;//4
char tel[15];//15
float scores[3];//12
char sex;//1
};//简单相加后为52字节
int main(){
struct Stu stu;
printf("%u",sizeof(stu));//实际为56字节
return 0;
}
二、结构体内存计算
-
在这之前,我们需要知道什么叫字节对齐?
字节对齐是指数据类型按照固定的字节大小排列,方便CPU和内存的读取。
而结构体内部的数据类型并不是都相等的,这个时候就需要字节对齐来提高计算机的读取效率。
-
结构体的默认存储方式是以结构体内部最大基本数据类型所占字节数进行对齐。
-
所以我们可以知道结构体所占内存大小为最大单成员类型的整数倍。
拿上面的结构体来说明:
struct Stu{ char name[20];//20 unsigned int age;//4 char tel[15];//15 float scores[3];//12 char sex;//1 };上面的变量中最大的数据类型是
int和float,都是4个字节灰色部分为未被使用的字节空间,所以我们容易知道结构体实际所占字节大小为56字节。

注意:所有数据类型的大小在内存中存储的地址一定是它的类型的倍数。
由上图可知,如果要进行优化,可把第三行的char放到第二行未被使用的一字节,即
char sex与float scores[3]交换位置,节省了一个字节的空间,所以优化后字节为54。一般来说按照数据类型所占字节从大到小的方式排列结构体内部元素来最大节省空间。
但是这种情况以double或float在前面的情况下可能不利于阅读,比如正常下是先char姓名后float分数,若按上面规则为先分数后姓名。
三、为什么需要字节对齐
我们对比对齐访问与不对齐访问,对齐访问牺牲了内存空间以换取速度性能,而非对齐访问牺牲访问速度性能以换取对内存空间的完全利用。
说的明白点,就是为了访问结构体成员效率更高,能够更高效的读取数据,虽然会牺牲一点点内存。
四、自定义字节对齐
通过对齐命令
#pragma pack(n):以n字节对齐
#pragma pack():取消指定对齐,恢复缺省对齐
若是#pragma pack(1)即以1字节对齐,不存在未被使用的字节空间。

五、位域
- 定义:将一个字节分为几个段,每一段表示一个对象,这样一个字节就可以表示多个对象。
struct BF
{
int a:8;//位域长度不能超过int的长度32bit
int b:2;
int c:6;
};
-
位域的存储
当使用有符号类型来定义位域,并且无意中使用到了正负(有意或者无意)特性时,就会出现问题。
struct BitField{ char a : 2; char b : 3; char c : 3; }; int main(){ struct BitField BF; // 位域赋值 BF.a = 03; // 11 BF.b = 05; // 101 BF.c = 02; // 010 printf("%d,%d,%d\n", BF.a, BF.b, BF.c); // OUTPUT: -1(0xff, 1111 1111), -3(0xfd, 1111 1101), 2(0x02, 0000 0110) // 可见,当为域的最高位是1的时候,会进行符号扩展,而且这也取决于编译器的实现 // 因此,为避免此类问题,最好使用无符号类型定义位域 // 如果把BitField中的char换成unsigned char就没有问题了,输出是3, 5, 2 }从其内存布局可以看出,使用位域的最佳实践是:
第一,位域的类型要使用无符号类型,并且在整个结构体内部要保持一致;
第二,位域的总长度尽量与类型的长度保持一致;
如下代码所示:
struct BitFieldDemo { unsigned char a : 1; unsigned char b : 3; unsigned char c : 4; };
本文探讨了C++和C语言中结构体的大小计算,深入讲解了字节对齐的原理和作用,包括结构体内存计算、自定义字节对齐策略。此外,还介绍了位域的概念,强调了位域使用时的最佳实践,如采用无符号类型并保持长度一致。
2744

被折叠的 条评论
为什么被折叠?



