昆明自助建站模板宁德网页设计制作

当前位置: 首页 > news >正文

昆明自助建站模板,宁德网页设计制作,网站界面设计规则,如何做网站背景前言#xff1a;C语言中拥有三种自定义类型#xff0c;这三种自定义类型是怎么运用呢#xff1f;在内存中又是怎么存储的呢#xff1f;通过这篇文章我们来逐个讲解讲解。 三种类型分别是#xff1a; 1.结构体 – 通俗的来讲就是可以把不同类型的变量放在一个集合中 2.枚举…前言C语言中拥有三种自定义类型这三种自定义类型是怎么运用呢在内存中又是怎么存储的呢通过这篇文章我们来逐个讲解讲解。 三种类型分别是 1.结构体 – 通俗的来讲就是可以把不同类型的变量放在一个集合中 2.枚举 – 可以理解成把所有事情全部列举出来 3.联合体 – 通俗的理解成成员公用一块空间的结构体 自定义类型 结构体结构体的声明匿名结构体类型结构体的自引用结构体的定义与初始化结构体在内存中的存储方式为什么存在内存对齐?结构体传参 位段位段的跨平台问题 枚举枚举的优点 联合共用体联合体在内存中的存储联合大小的计算 结构体 结构体的声明 struct Stu {char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号 }; //分号不能丢我们在声明结构体的时候也可以选择匿名通俗的讲就是这个结构体我们只会用一次 匿名结构体类型 //匿名结构体类型 struct {int a;char b;float c; }x;在这之前我们学过“循环”“判断语句”这些都是可以嵌套使用的在结构体当中我们是否也可以嵌套使用结构体呢就是在结构体中在引用结构体。 结构体的自引用 struct Node {int data;struct Node* next; };我们可以发现我们通过结构体指针的方式在结构体中引用结构体的方式可以来实现结构体的自引用。 结构体的定义与初始化 struct Point {int x;int y; }p1; //声明类型的同时定义变量p1 struct Point p2; //定义结构体变量p2 //初始化定义变量的同时赋初值。 struct Point p3 {x, y}; 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};//结构体嵌套初始化结构体在内存中的存储方式 要了解结构体的存储方式首先我们来看一看它是否和我们平常想象的方式一样在内存中存储。 例题 struct S1 {char c1;int i;char c2; };int main() {printf(%d\n, sizeof(struct S1));return 0; }分析 按照我们平常的逻辑char c1占1个字节int i 占4个字节char c2占一个字节总的加一起应供占6个字节我们来看看运行的结果是不是六个字节呢 运行结果 因此我们可以知道实际上他并不是像我们想象的方式在内存中存储的那是怎么存储的呢 首先得掌握结构体的对齐规则 第一个成员在与结构体变量偏移量为0的地址处。其他成员变量要对齐到某个数字对齐数的整数倍的地址处。 对齐数 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8结构体总大小为最大对齐数每个成员变量都有一个对齐数的整数倍。如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处结构体的整 体大小就是所有最大对齐数含嵌套结构体的对齐数的整数倍。 这段话又是什么意思呢我们接着上面的代码继续分析 我们把下图比作内存条第一个成员在与结构体变量偏移量为0的地址处故名思意就是代码的起始处。如图所示的C1就是所放的位置其他成员变量要对齐到某个数字对齐数的整数倍的地址处。 对齐数 编译器默认的一个对齐数与该成员大小的较小值因为int类型的的大小是4与VScode下的默认对齐数8相比较小因此int i必须放在内存位置4个倍数的位置如下图中所放的位置同理C2也是如此可是最后的结果如图加起来应该是9为什么结果是12呢我们看 结构体总大小为最大对齐数每个成员变量都有一个对齐数的整数倍。因为此结构体中最大对齐数是4因此我们的内存总大小是12和我们刚刚所对的运行结果刚刚好一样因此我们可以通过这个结构体的对齐规则来掌握结构体在内存中的存储。 为什么存在内存对齐? 平台原因(移植原因) 不是所有的硬件平台都能访问任意地址上的任意数据的某些硬件平台只能在某些地址处取某些特定类型的数据否则抛出硬件异常。 性能原因 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于为了访问未对齐的内存处理器需要作两次内存访问而对齐的内存访问仅需要一次访问。 我们也可修改默认对齐数来控制结构体中内存的存储。 #pragma pack(8)//设置默认对齐数为8 struct S1 {char c1;int i;char c2; }; #pragma pack()//取消设置的默认对齐数还原为默认通过这个例子我们知道了通过 pragma来控制默认对齐数那么我们来一道例题。 #include stdio.h #pragma pack(8) struct S1 {char c1;int i;char c2; }; #pragma pack() #pragma pack(1)//设置默认对齐数为1 struct S2 {char c1;int i;char c2; }; #pragma pack()//取消设置的默认对齐数还原为默认 int main() {//输出的结果是什么printf(%d\n, sizeof(struct S1));printf(%d\n, sizeof(struct S2));return 0; }如图所示 S1和上个例题相同就不再过多讲解在S2中我们的默认对齐数是1可知C1放在起始处后面可以根据依次连接的方式对齐。因此结果是12、6 因此我们可以知道结构在对齐方式不合适的时候我么可以自己更改默认对齐数。 结构体传参 我们直接看代码 struct S {int data[1000];int num; }; struct S s {{1,2,3,4}, 1000}; //结构体传参 void print1(struct S s) {printf(%d\n, s.num); } //结构体地址传参 void print2(struct S* ps) {printf(%d\n, ps-num); } int main() {print1(s); //传结构体print2(s); //传地址return 0; }请问上面的 print1 和 print2 函数哪个好些 答案是首选print2函数。 原因 函数传参的时候参数是需要压栈会有时间和空间上的系统开销。 如果传递一个结构体对象的时候结构体过大参数压栈的的系统开销比较大所以会导致性能的下降。在print1中是将整个结构体传过去而print2中是传的地址因此print2会大幅度提高性能 位段 位段的成员可以是 int (unsigned int) (signed int) 或者是 char 属于整形家族类型。位段的空间上是按照需要以4个字节 int 或者1个字节 char 的方式来开辟的。位段涉及很多不确定因素位段是不跨平台的注重可移植的程序应该避免使用位段。位段的声明和结构是类似的但是位段的成员名后边有一个冒号和一个数字。 例如 struct A {int _a : 2;int _b : 5;int _c : 10;int _d : 30; }s1;int main() {printf(%d\n, sizeof(struct A));return 0; }代码分析 因为位段的空间上是按照需要以4个字节 int 或者1个字节 char 的方式来开辟的。 声明类型是int类型的所以一开始开辟了4个整型的空间即32bit_a用掉了2bit,还剩下30bit_b用掉了5bit,还剩下25bit_c用掉了10bit,还剩下15bit_d需要30bit可是我们所剩下的空间只有15bit又因为_d是int类型因此在开辟一个整型的空间即32bit位将_d所需要的30bit位全部放进去因此我们可以知道一共开辟了8个字节的空间。 运行结果 位段的跨平台问题 int 位段被当成有符号数还是无符号数是不确定的。位段中最大位的数目不能确定。16位机器最大1632位机器最大32写成27在16位机 器会出问题。位段中的成员在内存中从左向右分配还是从右向左分配标准尚未定义。当一个结构包含两个位段第二个位段成员比较大无法容纳于第一个位段剩余的位时是舍弃剩余的位还是利用这是不确定的。 枚举 枚举顾名思义就是一一列举。 把可能的取值一一列举。 比如我们现实生活中 一周的星期一到星期日是有限的7天可以一一列举。 看代码 enum Day//星期 {Mon,Tues,Wed,Thur,Fri,Sat,Sun }; enum Sex//性别 {MALE,FEMALE,SECRET } enum Color//颜色 {RED,GREEN,BLUE };当然这些可能取值都是有值的默认从0开始一次递增1当然在定义的时候也可以赋初值。 我们来看一道题目带你懂枚举的值 enum ENUM_A {X1,Y1,Z1 255,A1,B1, }; int main() {enum ENUM_A enumA Y1;enum ENUM_A enumB B1;printf(%d %d\n, enumA, enumB);return 0; }代码分析 我们刚刚讲过默认从0开始一次递增1当然在定义的时候也可以赋初值。那么这题的答案是多少我们不难看出X1是从0开始故而Y1是1然而Z1我们给它赋了初始值255那B1是多少是在255的基础上加2还是接着前面的01开始呢看运行结果 显然我们可以在知道B1的值是在接着我们所赋的值来继续加1因此我们也了解了枚举的作用。 枚举的优点 增加代码的可读性和可维护性和#define定义的标识符比较枚举有类型检查更加严谨。防止了命名污染封装便于调试使用方便一次可以定义多个常量 联合共用体 定义联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员特征是这些成员公用同一块空间所以联合也叫共用体。 口头的文字肯定不如代码的效果明显所以我们直接上代码 //联合类型的声明 union Un {char c;int i; }; //联合变量的定义 union Un un;联合体在内存中的存储 联合的大小至少是最大成员的大小。当最大成员大小不是最大对齐数的整数倍的时候就要对齐到最大对齐数的整数倍。 例题 union Un {int i;char c; }; union Un un;int main() {// 下面输出的结果是一样的吗printf(%d\n, (un.i));printf(%d\n, (un.c));return 0; }前面我们说过特征是这些成员公用同一块空间所以联合也叫共用体。 因此我们可以知道题目是公用一块空间。
联合大小的计算 例题 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; }代码分析 Un1的分析占8个字节 Un2的分析占16个字节 好了今天的讲解就到这里了如果有讲的不好的地方希望各位多多指出。