上一篇我们说了好多关于结构体和链表的知识,但是还有一些知识我还是没有说到,今天来给大家梳理一下。
一,柔性数组
一,定义
c99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。
下面有两种定义方式。下标可以不写,也可以写作0;
struct s1 { int i; int a[]; }; struct s2 { int i; int a[0]; };
二,特点
三,使用
首先先看下面这两段代码
1.
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> typedef struct s1 { int i; int a[]; }pnode; int main() { //期待a数组的大小是10个整形 pnode* ps = (pnode*)malloc(sizeof(pnode)+10*sizeof(int)); //判断 if (!ps) exit(1); //初始化数据 ps->i = 10; for (int i = 0; i < 10; i++) { ps->a[i] = i; } //空间不足,运用realloc函数来增加。 pnode* pss = (pnode*)realloc(ps,sizeof(pnode) + 20 * sizeof(int)); //如果,分配内存成功,就把该指针的地址给ps, //要搞清楚这个函数的用法 if (pss != NULL) { ps = pss; } //使用 //释放 free(ps); //释放后一定要记得置空 ps = NULL; return 0; }
2.
#define _CRT_SECURE_NO_WARNINGS 1 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> typedef struct s1 { int i; int *a; }pnode; int main() { pnode* ps = (pnode*)malloc(sizeof(pnode)); //判断 if (!ps) exit(1); //a此时并没有指向,是一个野指针,故现在要对其申请内存空间 //给它10个整形的空间 //这个地方也有不同的地方 //这里是为了结构体指针变量ps指向的a指针申请内存空间,强制转换为int*类型。 ps->a = (int *)malloc(10 * sizeof(int)); //判断 if (ps->a == NULL) { exit(1); } //初始化数据 ps->i = 10; for (int i = 0; i < 10; i++) { ps->a[i] = i; } //空间不足,运用realloc函数来增加。这里我们需要单独对ps->a的地址来进行申请 int* ptr = (int*)realloc(ps->a, 10 * sizeof(int)); //判断,如果分配内存成功,就把该指针的地址给ps, if (ptr != NULL) { ps->a = ptr; } //使用 //释放 //这里的释放是有东西的,要先释放ps->a,因为如果先释放了ps,*a就成了野指针,他指向的空间已经被释放了,故不能再次使用。 free(ps->a); ps->a = NULL; free(ps); ps = NULL; return 0; }
注意:
根据上面的两段代码,我们来看一看realloc这个函数。
ptr必须是以前通过动态存储分配得到的指针。size是现在需要的空间大小,如果分配失败,返回NULL,同时原来ptr指向的存储块的内容不变。
如果成功,返回一片能存放大小为size的区块,并保证该块的内容与原块一致,如果size小于原来的区块大小,则内容为原块前size范围内的内容。如果新块更大,则原有数据存在新块的前一部分。后一部分记得要初始化。
如果分配成功,原存储块的内容就很可能被改变了,因此不允许再使用ptr去使用它。
二,位段
成员也可以是char,char类型也是以整型的方式存储的。
这个数字代表的是比特
该位段的大小为8个字节,由上可知,位段的大小空间开辟是根据需求以四个字节(int)或者一个字节(char)的方式来开辟的。
刚开始的时候开辟了四个字节用来存放,存完三个变量后,剩余的空间已经不足以用来存放d变量,故,需要再开辟一个int的空间,用来存放,所以,该位段的大小是8个字节。
再看,存放完a,b,c三个变量后,剩余15个比特位,C语言中并未说明这剩余的部分到底使不使用,所以一般使用位段时是不跨平台的。
需要注意的是,成员变量的大小不能大于一个int,因为充满了太多的不确定因素。
三,枚举和联合体(共用体)
一,枚举
顾名思义,就是一一列举。
括号中间的就是它的成员常量,是枚举可能的取值,并不是说都会取到,一次使用只能取到其中一个常量。
枚举类型的成员都是常量,通常是指无符号整型,既然是常量,那么,他们都是有相应的值的。一般来说,都是从0开始,向后递增
我们也可以根据自己的需要,对成员常量进行赋值。
那我们为什么要使用枚举呢?
对于具体而言,枚举到底有什么用?以下面这个计算器的模拟为例。我设置了一个枚举类型,使程序的可读性增加。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> void menu(void) { //提供菜单 printf("************请选择****************\n"); printf("******1.Add*********2.Sub*********\n"); printf("******3.Mul*********4.Div*********\n"); printf("************0.exit****************\n"); printf("**********************************\n"); } //写计算器要用的函数 int Add(int x, int y) { return x + y; } int Sub(int x, int y) { return x - y; } int Mul(int x, int y) { return x * y; } int Div(int x, int y) { return x / y; } enum s{ exit, Add, Sub, Mul, Div } int main() { //因为计算器可以反复使用,所以用循环来写 //至于用while还是do-while都是看自己喜好 do { //在循环中定义变量,每次循环开始时初始化数据,就不会影响后面的运算 int x, y, ret; x = y = ret = 0; int input=0; //输出菜单 menu(); //选择函数来运行程序,达到自己的目的 printf("请选择:>"); scanf("%d",&input); //因为选项很多,太过复杂。选择用switch结构 switch (input) { case Add: printf("请输入2个操作数:>"); scanf("%d%d", &x, &y); //调用函数 ret = Add(x, y); printf("ret=%d\n", ret); break; case Sub: printf("请输入2个操作数:>"); scanf("%d%d", &x, &y); ret = Sub(x, y); printf("ret=%d\n", ret); break; case Mul: printf("请输入2个操作数:>"); scanf("%d%d", &x, &y); ret = Mul(x, y); printf("ret=%d\n", ret); break; case Div: printf("请输入2个操作数:>"); scanf("%d%d", &x, &y); ret = Div(x, y); printf("ret=%d\n", ret); break; //如果输入为0,程序结束,直接跳出 case exit:printf("程序结束"); break; //如果输入的是其他数,给用户继续选择的机会。 default:printf("选择错误,请重新选择"); break; } } while (input); return 0; }
还有一些操作
我们可以直接把枚举类型变量定义为某一个变量,但是,不能直接赋值为一个整数,类型转换会导致数据的丢失。
也可以写一个限定符,限定blue就是枚举类型s的一个成员。
我们要知道的是,枚举类型的成员变量只能是int或者size_t int,所以,不管枚举类型中含有多少个成员,大小永远是4;
二,联合体(共用体)
一,定义
二,特点
我们尝试来给新建的联合体变量赋值,我们发现,i和c里面存放了相同的值,这也就是所谓的共用体。
在联合体中最好是不要单独对其中的每个元素赋值,会影响其他成员的值。
三,联合体的计算
以这个题目为例,这个联合体的大小就是8.
好了,大概就说这么多,已经足够大家使用和学习了。
祝诸君学业有成,诸事顺利。