1.结构体补充
昨天结构体有一些忘写了,让我们先把昨天的结束。
昨天讲了结构体的内存对齐,如果大家不知道这个重要的概念,可以去看这篇结构体内存对齐(你可能不知道但很重要的知识)-CSDN博客
内存对齐
上篇文章我们说了vs中默认对齐数是8,但是这个是可以改的,具体方法就是
#include<stdio.h>
#pragma pack(1)//将对齐数改为1
struct a {
char a1;
int a3;
char a2;
};
#pragma pack()//还原为默认对齐数
struct b {
char b1;
int b3;
char b2;
};
int main() {
printf("%u\n%u", sizeof(struct a), sizeof(struct b));
}
同理在c++类中也一样:
cpp
#include<iostream>
using namespace std;
#pragma pack(1)
class a {
char a1;
int a3;
char a2;
};
#pragma pack()
class b {
char b1;
int b3;
char b2;
};
int main() {
cout << sizeof(a) << "\n" << sizeof(b);
return 0;
}
结构体位段
- 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以
选择其他类型。
- 位段的成员名后边有⼀个冒号和⼀个数字。
cpp
#include<stdio.h>
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
int main() {
return 0;
}
他的大小是多少呢?大家不妨猜猜看
为什么是8呢?我们需要先明白一个道理,他在类型后面加的数字相当于重新定义这个变量占几个比特,所以a占2个,b占5个,c占10个,d占30个,对于位段,我们有这样的规则
1. 位段的成员可以是 int , unsigned int , signed int 或者是 char 等类型。
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
既然我们的数据类型是按int来开辟的,一次开辟4个字节,就是32个比特,a放进去,b放进去,c放进去,还有15个比特位没有放,但这时,已经不够放d了,所以我们重新开辟一个字节的空间去放d,所以最后大小就是8个字节
位段的弊端:
int位段被当成有符号数还是⽆符号数是不确定的。
位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会
出问题。
- 位段中的成员在内存中从左向右分配,还是从右向左分配,标准尚未定义。4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃
剩余的位还是利⽤,这是不确定的。
总结:跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。
2.联合体与枚举
1.联合体
cpp
#include<stdio.h>
union u {
char a;
int b;
};
int main() {
printf("%u", sizeof(union u));
return 0;
}
联合体与结构体类似,声明的关键字是union,但区别就是,联合体是所有成员共用一片存储空间
接着我们把地址打出来可以发现,他所有元素的地址都一样,也证实了我们前面说的话
联合体的大小:
1.联合的⼤⼩⾄少是最⼤成员的⼤⼩。
2.当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。
cpp
#include <stdio.h>
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
int main()
{
//下⾯输出的结果是什么?
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
return 0;
}
答案是8和16,why?首先先看un1,char数组占了5个字节,加上int共用还是5个,接着char的对齐数是1,int的对齐数是4,所以最后大小应是4的整数倍,就是8.
un2,short数组占14个字节,int占4个,共用14个,short对齐数是2,int是4,最后应是4的整数倍,所以最后是16.
联合体的使用最明显的优势就是节省空间了,⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、⻚数
杯⼦:设计
衬衫:设计、可选颜⾊、可选尺⼨
然后我们写出了这样的代码:
cpp
struct gift_list1
{
//公共属性
int stock_number;//库存量
double price; //定价
int item_type;//商品类型
//特殊属性
char title[20];//书名
char author[20];//作者
int num_pages;//⻚数
char design[30];//设计
int colors;//颜⾊
int sizes;//尺⼨
};
struct gift_list2
{
int stock_number;//库存量
double price; //定价
int item_type;//商品类型
union {
struct
{
char title[20];//书名
char author[20];//作者
int num_pages;//⻚数
}book;
struct
{
char design[30];//设计
}mug;
struct
{
char design[30];//设计
int colors;//颜⾊
int sizes;//尺⼨
}shirt;
}item;
};
int main() {
printf("%u\n%u",sizeof(struct gift_list1), sizeof(struct gift_list2));
return 0;
}
最后,我们尝试用联合体判断大小端。
cpp
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;//返回1是⼩端,返回0是⼤端
}
把un.i改为1,他的存储应该是00 00 00 01,如果是低位存储的话,应该此时char和int共用01这段空间,所以un.c也被改成了1,反之高位的话,char占据的是00的空间,是不变的。
2.枚举
枚举类型的关键字是enum
cpp
#include<stdio.h>
enum sex {
male,
women,
secret
};
int main()
{
return 0;
}
其中male代表0,women代表1,secret代表3,这个是默认的,当然我们也可以人为改变。
枚举的优点:
增加代码的可读性和可维护性
和#define定义的标识符⽐较枚举有类型检查,更加严谨。
便于调试,预处理阶段会删除 #define 定义的符号
使⽤⽅便,⼀次可以定义多个常量
枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤
那我们是否可以用整形给枚举变量赋值呢?
在c语言中可以,但c++类型检查更加严格,就不行了
在c++中,
他就会报错
好的,就到这里了,大家中秋快乐鸭,有帮助的还请点个赞