目录
对于类型有这样的分法:
内置类型:
- char
- short
- int
- long
- long long
- float
- double
自定义类型:
- 结构体
- 枚举
- 联合
结构体:
当我们描述一个对象 但发现他有许多属性时 这时候这些属性就是结构体的成员变量 这些成员变量可以时不同的类型 也就时有着不同的属性
经典的结构体是这样的
struct stu //stu 是标签 因为有时我们需要用到多个结构体 那么标签就是为了区别他们
{ 如果不要标签 那他就是匿名结构体 后面会说到
memberlist 成员列表
}variablelist 变量列表
s1 s2 s3
如果你对把一个结构体赋值给另一个结构体 即使两个结构体成员变量相同 但他们时匿名结构体的话 那么是不可以的 因为编译器会认为你左右的两个类型时不同的
定义的方法:
struct stu
{
~~~~~~
}s1={~~~~};
int main()
{
struct stu s2={~~~~~~};
}
结构体的内存对齐:
在了解结构体的内存对齐之前首先我们要知道一个 宏 offsetoff
这个可以返回一个结构体成员在结构体里面偏移量的大小 记住他的头文集 <stddef.h>
#include <stdio.h>
#include <stddef.h>
struct stu
{
char i;
int c;
}s1;
int main()
{
printf("%d",offsetof(struct stu,i)); //0
printf("%d",offsetof(struct stu,c)); //4
}
我们再定义一个结构体来解释这个现象
我们先看看内存对齐的规则
①第一个成员在结构体偏移量为0的地址处
②其他成员变量(从第二个变量开始)要对齐到某个数字(对齐数)的整数倍的地址处
对齐数是编译器默认的一个对齐数 与 该成员大小的 最小值
vs默认对齐数是 8
③结构体的总大小为最大对齐数的整数倍
④如果嵌套了结构体的情况 嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体里面的对齐数)的整数倍
(linux没有默认对齐数 对齐数就是其自身的大小)
为什么存在内存对齐?
平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能再某些地址处去某些特定的类型数据
性能原因:数据结构(尤其是栈)应该尽可能的在自然边界上对齐
其实总的来讲内存对齐 就是 空间换时间的做法
同样默认对其数是可以修改的 使用 #pragma pack(8) 就可以修改默认对齐数为8
使用 #pragma pack()还原默认对齐数
其次对于结构体传参 还是传地址的要好
位段:
位段的声明和结构是类似的 但还是有不同的
位段的成员必须是int,unsigned int,signed int 但这只是规定 char也没什么事
位段的成员名后边有一个冒号和数字
位段的内存分配:
- 位段的空间是按照需要以4个字节或者1个字节的方式来开辟的
- 位段涉及很多不确定的因素,位段是不跨平台的,注重可执行程序应该避免使用位段
位段很大的作用就是可以帮助我们优化 节省空间 有些变量只要那么多的bit位 我们就给他那么多 这样就可以很好的节省空间
#include <stdio.h>
struct stu
{
char a:3;
char b:4;
char c:5;
char d:4;
}s;
int main()
{
s.a=10;
}
这种给有位段的结构体成员变量赋值时 也要注意位段的限制
好像这里给a赋值10 他的二进制码 1010 但是在赋值时 我们分配给a的是三个bit位 那么存放时就只能放三个bit位的值 也就是是说只能放 010 进去
对于连续存放时 从地位往高位 放入
位段的跨平台问题:
① int 位段被当成有符号数还是无符号数是不确定的
②位段中的最大数目不能确定(早期有些机器是是16位机器 最大数目是16 32位机器的最大数目是32)所以如果一个机器是16位的位段设置为 大于它的 就会出问题
③ 位段中的成员在内存中是从左向右分配 还是从右向左分配 这个也是没有定义的
④ 当一个结构体包括两个位段,如果第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃还是利用,这是不确定 (这就跟我们说的一样)
总的来讲 位段是不支持跨平台的
枚举类型:
枚举顾名思义 就是 列举 把一些数据列举出来
enum u
{
//枚举的可能取值
EXIT,0 这只是初始的
MON, 1
THE, 2
WED 3
};
enum s
{
DAY=7,
ONE=6, 这时候FOR为7
FOR
};
int main()
{
enum u d = EXIT;
enum u d = 0;//这样赋值是不建议的 在c语言里面它可能没有报错 但在c++里面检查更严格后就会报错了
//因为c++会认为 d是枚举类型 0是int类型 不会让他们这样赋值
}
注意 枚举成员都是常量
枚举看起来虽然功能单一 但它也是有优点的
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型 也就是说枚举有类型检查,更加严谨
- 防止了命名污染(封装)
- 便于调试
- 使用方便,一次可定义多个常量
联合体:
union u
{
char c;
double i;
};
int main()
{
printf("%d\n",sizeof(u)); 8 最大成员的大小
printf("%p\n",&u);
printf("%p\n",&(u.c));
printf("%p\n",&(u.i));
//后面的地址是一样的
}
可以发现 c 和 i 的其实地址是一样的 使用的第一个字节重复了 所以联合体还有个名字叫共用体 当然共用体也有个缺点 就是同一时间只能使用一个变量 每次使用一个变量 另一个变量也会跟着改变所以 每次只能使用一个变量
所以联合体就有了一个应用场景 就是当你每次使用时 都会用到不同的东西 但每次只使用一个的时候 就可以用了
知道了这种特性后 我们就可以用这个特性来判断我们的机器是大端还是小端
int Checksys()
{
union u
{
char i;
int c;
}s;
s.c=1;
return s.i;
}
联合体大小的计算:
联合的大小至少是最大成员的大小。
当最大成员的大小不是最大对齐数的整数倍时,就要对齐最大对齐数的整数倍
大家来计算一下这个联合体的大小是多少
union un
{
short c[7];
int i;
};
union un
{
short c[7]; 2-8-2
int i; 4-8-4
};
最终最大对齐数是4 也就是要4的倍数 其中最大成员大小是14 那么最终的联合体大小是16
那么到这里 我们自定义类型的讲解就没有了 感谢大家能看到这里 希望大家都能收到自己心仪的offer!!!!