目录
一、结构体
1.内存对齐
何为内存对齐呢?
编译器为程序中的每个“数据单元”安排在适当的位置上。
对于大部分程序员来说,内存对齐是“透明”状态的,它归属于编译器管辖,但计算结构体大小,我们就必须清晰了解内存对齐。
以下是内存对齐的规则:
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));
在不了解内存对齐之前,依照我们的惯性思维来计算的话,此结构体大小应该是1+4+1=6个字节大小,但我们在编译器底下运行一遍,却发现结构体大小实际值却不是这个值,而是12字节。
为什么?依照上面的对齐规则,c1是1个字节大小,对齐数为1,默认对齐数为8(vs),所以变量c1的对齐数为较小值1,同理,i的对齐数为4,c2的对齐数为1,整个结构体的对齐数就是三者之间的最大值4。但是按照对齐规则,结构体总大小为最大对齐数的整数倍,此处取8肯定不可取,因为在分配完三个变量的大小之后,结构体已经占用了九个字节的内存大小,其中被黑色×掉的是无用(被主动浪费的内存单元)后文会讲这样做的原因。所以结构体大小便是12。
所以为什么结构体中存在内存对齐呢?其实这是一种 以空间换取时间的思维。
官方解释如下:
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
例如相同的代码,struct S2便更优,占用空间更少。
2.修改默认对齐数
我们需要使用预处理指令(后续博客会补充)
#pragma pack(n)n为修改后的默认对齐数的值。
结构在对齐方式不合适的时候,我么可以自己更改默认对齐数。
3.结构体传参
在结构体传参的时候,我们有传参和传址两种选择。
但是:
所以一般我们都选择传址。
二、位段
1.位段与结构
位段与结构体的声明十分相似,大致有两点区别
struct A {
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
此处,A便是一个位段。
2.计算位段内存大小
算算上代码中位段的内存大小
_a占两个bit,_b占五bit,int开辟32bit,可存下_a,_b,_c,存不下_d,再开辟一个字节,存放_d,所以此位段占两个int大小,即8个字节。
3.位段的跨平台问题
三、联合体(共用体)
union Un
{
char c;
int i;
};
算算它的大小
补充:联合体大小计算规则
结果为4字节。
union Un
{
char c;
int i;
};
union Un un;
int main()
{
printf("%d\n", sizeof(union Un));
printf("%p\n", &un);
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
return 0;
}
我们打印它的地址
可以发现联合体内变量使用的是同一块内存空间
2.联合体的应用范围
例如上图的应用场景,一个成员的组成元素只能是两个中的一个,这时使用联合体可以大大节省空间。
四、枚举类型
1.介绍
枚举就是,把可能的取值一一列举。
定义一个枚举类型时,第一个元素默认值为0,然后第二个元素为1...依次递增
但我们也可以给枚举变量赋值,使得枚举更加灵活方便,例如:
enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};
2.枚举的优点
欢迎多多交流,一起进步!