一、结构体
1、结构体的声明
如下struct是结构体的声明,里面可以放很多个不同类型的数据,而结构体就是这些值的集合,这些值又称为结构体的成员。
如下面就是利用结构体去设定一个学生的信息,如姓名,年龄这些都不是一样的数据类型,但是可以放在一个结构体类型中,从而去一下定义不同类型的数据了,这样就可以方便使用。
int main() { struct Stu { char name[20];//姓名 int age;//年龄 char sex[5];//性别 char id[20];//学号 }; return 0; }
还用一种声明就是特殊的声明,如下这种特殊的声明就是省略标签名的声明,只定义成员变量却不定义变量名的声明。
struct { int a; char b; float c; }x; struct { int a; char b; float c; }a[20], *p;
2、结构体的自引用
结构体的成员是可以放很多种的类型,所以能不能把结构体放入结构体呢,当然也是可以的,但是不能嵌入同类型的,例如下方代码1这样就是不能使用的。但是可以使用结构体指针,例如代码2就是使用结构体指针使用的嵌套。
如果没有结构体指针的话,他就会一直嵌套停不下来,所以需要一个指针结束,且必须在末尾。
//代码1 struct Node { int data; struct Node next; }; //代码2 struct Node { int data; struct Node* next; };
3、结构体变量的定义和初始化
既然我们了解什么是结构体的声明和如何嵌套,那么我们可以进行变量的定义和初始化了,如下方代码第一个Point就是一个结构体变量,然后在大括号后面的p1就是在声明结构体变量的同时定义了变量p1,而怕p2就是根据Point这个结构体类型进行定义的第二个结构体变量,这个结构体的成员和p1一样,p3就是在定义p3变量的同时进行赋值,把结构体成员的参数赋值为0。
struct Stu s这个结构体变量的成员中是不同的类型,然后在进行这个变量的传参时,第一个因为时字符类型,然后就赋值了字符串“zhangsan”,年龄是整数,所以就赋值整数。
struct Node n2就是嵌套结构体的赋值,首先把这个结构体本身的data变量赋值10,然后第二个是嵌套的结构体变量,它里面有两个变量,这时需要用大括号引用进行嵌套赋值,最后把结构体指针赋值
为NULL,这就是结构体指针嵌套的赋值。
struct Point { int x; int y; }p1; //声明类型的同时定义变量p1 struct Point p2; //定义结构体变量p2 //初始化:定义变量的同时赋初值。 struct Point p3 = {0, 0}; struct Stu //类型声明 { char name[15];//名字 int age; //年龄 }; struct Stu s = {"zhangsan", 20};//初始化 struct Node { int data; struct Point p; struct Node* next; }n1 = {10, {4,5}, NULL}; //结构体嵌套初始化 struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
4、结构体内存对齐
结构的内存对齐是一个比较难的知识点,在不同的编译环境下也各有不同的默认对齐数,他的对齐规则是从第一个成员开始如下图所示,可以看出第一个是double类型所以和vs的默认对齐数相比,将取最小,然后占用8个字节char类型和默认对齐数相比,1最小所以是1的倍数可以占用从8开始占用一个字节,而int类型是4比默认对齐数小,需要占用4个字节,从4的倍数,开始也就是12开始,所以这个结构体变量占用了16个字节,最后这个地址大小就是这个结构体成员的最大对齐数的倍数。
为什么会有内存对齐?大部分人参考资料是这么说的
1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
从上图可以看出中间有的字节没有使用,我们也可以使用#pragma 这个指令来进行修改默认对齐数,使得空间占用更加紧凑。
二、位段
1、什么是位段
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。
位段后面的冒号代表占用了多少位字节,表示占多少bit位的大小,且设置的大小不能超过原类型最大的大小,例如int 为 4 字节,设置的数字就不能超过 32 。
2、位段的跨平台问题?
1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
舍弃剩余的位还是利用,这是不确定的。
三、枚举
1、枚举的定义
枚举顾名思义就是一个个举例如下方代码
就是从0开始一个个加1,也可以自己定义成员的数值。
enum Day//星期 { Mon, Tues, Wed, Thur, Fri, Sat, Sun }; enum Sex//性别 { MALE, FEMALE, SECRET }; enum Color//颜色 { RED, GREEN, BLUE };
2、枚举的优点与使用
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量
使用就如下代码可以直接判断这个数值。
enum Day//星期 { Mon, Tues, Wed, Thur, Fri, Sat, Sun }; int main() { if(Mon==0) { printf("今天是星期一"); } return 0; }
四、联合体
联合体也叫做共用体定义方法如下。
//联合类型的声明 union Un { char c; int i; }; //联合变量的定义 union Un un; //计算连个变量的大小 printf("%d\n", sizeof(un));
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为
联合至少得有能力保存最大的那个成员)。
联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。