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

做导航网站成本淘金网站建设推广

做导航网站成本,淘金网站建设推广,简单企业网站源码,wordpress扫码付费可见目录 结构体 结构体类型的声明 匿名结构体 结构的自引用 结构体变量的定义和初始化 结构体成员变量的访问 结构体内存对齐 结构体传参 位段 位段类型的声明 位段的内存分配 位段的跨平台问题 位段的应用 枚举 枚举类型的定义 枚举的优点 联合体(共用体) 联合…

目录

结构体

结构体类型的声明

匿名结构体

结构的自引用

结构体变量的定义和初始化

结构体成员变量的访问

结构体内存对齐

结构体传参

位段

位段类型的声明

位段的内存分配

位段的跨平台问题

位段的应用

枚举

枚举类型的定义

枚举的优点

联合体(共用体)

联合类型的定义

联合体的特点

联合体的大小

联合体的应用


结构体

结构体类型的声明

之前学习到的数据类型我们称之为内置类型,如int, double, char, float等,后续还学习了数组,数组是一组相同类型元素的集合,但描述一个事物通常需要用到不同的类型,比如要描述一个学生,有年龄,姓名,学号等,就会出现各种类型的字段,这些叫做结构体的成员,结构体的每个成员额可以是不同的变量类型!

struct Stu是一个自定义的结构体类型, struct是结构体关键字,Stu是结构体标签(tag)

struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
};

在使用时我们感觉结构体类型太长了,可以typedef进行类型重定义

typedef struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}stu;

匿名结构体

在声明结构体类型的时候,可以不完全的声明,也就是省略掉结构体标签, 称之为匿名结构体

struct
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
};

建议不要使用匿名结构体,可读性不是很好,就按照最标准的 struct + 结构体标签 来创建结构体

结构的自引用

在结构中包含一个类型为该结构本身的成员是否可以呢?

struct Node
{int data;struct Node next;
};

这样写是不可以的,因为sizeof(struct Node)是无法计算的,用该结构体类型创建变量时开辟的空间大小也就是未知的,因此是错误的写法

struct Node
{int data;struct Node* next;
};

这样写就是可以的,第二个成员变量是一个结构体指针,固定大小是4/8个字节, 这也是数据结构中链表的每个节点的结构体定义方式

typedef struct
{int data;Node* next;
}Node;

这样写是不可以的,因为定义结构体类型时第二个成员变量用到了typedef后的类型,而此时结构体还没有创建完成,而结构体要创建完成,第二个成员变量就应该定义完成了,这就是先有鸡还是先有蛋的问题了!

typedef struct Node
{int data;struct Node* next;
}Node;

这样写就是可以的,第二个成员变量定义时使用的是 struct Node, 已经有该类型了!

结构体变量的定义和初始化

●定义全局变量并初始化

#include <stdio.h>//声明结构体类型的同时初始化变量(定义+赋初值)
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}s1 = { "zhangsan", 18, "mail", "20221931" };   //全局变量struct Stu s2; //全局变量int main()
{s2 = { "lisi", 20, "femail", "31931313"};return 0;
}

●定义局部变量并初始化

#include <stdio.h>
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
};
int main()
{struct Stu s = { "wangmazi", 23, "mail", "39133113" };return 0;
}

● 结构体的嵌套定义

#include <stdio.h>
struct score
{int x;char ch;
};
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号struct score s; //嵌套结构体
};
int main()
{struct Stu s = { "wangmazi", 23, "mail", "39133113", {10, 'q'}};return 0;
}

结构体成员变量的访问

● 结构体变量.成员变量

● 结构体指针变量->成员变量

● (*结构体指针变量).成员变量

#include <stdio.h>
struct score
{int x;char ch;
};
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号struct score sc; //嵌套结构体
};
int main()
{struct Stu s = { "wangmazi", 23, "mail", "39133113", {10, 'q'}};//1.结构体变量.成员变量printf("%s %d %s %s %d %c\n", s.name, s.age, s.sex, s.id, s.sc.x, s.sc.ch);//2.结构体指针变量->成员变量struct Stu* p = &s;printf("%s %d %s %s %d %c\n", p->name, p->age, p->sex, p->id, p->sc.x, p->sc.ch);//3.*(结构体指针变量).成员变量printf("%s %d %s %s %d %c\n", (*p).name, (*p).age, (*p).sex, (*p).id, (*p).sc.x, (*p).sc.ch);return 0;
}

结构体内存对齐

现在我们来讨论一下结构体的大小,结构体中包含了若干个成员变量,结构体的大小是所有成员变量大小相加吗???

#include <stdio.h>
struct s1
{char c1;int i;char c2;
};
int main()
{printf("%d\n", sizeof(struct s1)); //12return 0;
}

显然不是,结构体中的成员变量并不是挨着连续存放的,而是要遵守一定的对齐规则!

结构体内存对齐规则

1.第一个成员在与结构体变量偏移量为0的地址处

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处

对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的一个对齐数值为8

3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

画图解释上面结构体的大小是12:

结构体嵌套计算大小:

#include <stdio.h>
struct S3
{double d;char c;int i;
};
struct S4
{char c1;struct S3 s3;double d;
};
int main()
{printf("%d\n", sizeof(struct S3)); //16printf("%d\n", sizeof(struct S4)); //32return 0;
}

计算出 struct S3 的所有成员的最大对齐数是8,因此struct S3 的起始位置就是8的整数倍,然后strcut s3 内部的成员变量存放规则依旧遵守前三条规则,最后检查整体结构体的大小是所有成员(包括嵌套结构体成员)大小的整数倍,也就是32个字节

为啥存在结构体内存对齐?

1. 平台原因(移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。

2. 性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

举个例子: 对于下列结构体:

struct S
{char c; int i;
};

总体来说: 结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:

让占用空间小的成员尽量集中在一起

struct S1
{char c1;int i;char c2;
};struct S2
{char c1;char c2;int i;
};

s1结构体和s2结构体的成员变量是完全一样的,但是s2结构体的两个char类型变量在一起,所以同样遵守结构体内存对齐规则前提下,s2是更加节省空间的!

修改默认对齐数

● 使用 #pragma pack() 预处理指令修改默认对齐数

#include <stdio.h>
#pragma pack(1) //修改默认对齐数
struct S1
{char c1;int i;char c2;
};
#pragma pack() //恢复默认对齐数int main()
{printf("%d\n", sizeof(struct S1)); //6return 0;
}

结构体传参

#include <stdio.h>
#include <stddef.h>
struct S
{int data[1000];int num;
};void print1(struct S ss)
{for (int i = 0; i < 3; i++){printf("%d ", ss.data[i]);}printf("%d\n", ss.num);
}void print2(const struct S* ps)
{for (int i = 0; i < 3; i++){printf("%d ", ps->data[i]);}printf("%d\n", ps->num);
}int main()
{struct S s = { {1, 2, 3}, 100 };print1(s);print2(&s);return 0;
}

代码中有两种传参方式,可以采用代码一(传值传参),也可以采用代码二(传址传参),那么使用哪一个好呢???

建议使用传址传参,理由如下:

1. 传值传参,参数需要压栈,会有时间和空间上的系统开销

2. 如果结构体对象过大,参数压栈的系统开销比较大,导致性能下降

3. 如果要修改外部的结构体,就只能传址传参了; 如果不想修改,传址传参的形参加上const即可

位段

位段类型的声明

1.位段的成员必须是 int、unsigned int 、signed int 、char 等等,  总之,必须属于整形家族

2.位段的成员名后边有一个冒号和一个数字(表示成员占几个比特位)

//位段
struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};

可以看到,位段是一种节省空间的方式,int是占据4个字节,32个比特位,但是比如int flag 变量,用来标识真假,我们只需要两种状态,01 / 10, 只需要两个比特位就够了,也有很多其他类似的场景,因此使用位段可以节省空间

位段的内存分配

1. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。

#include <stdio.h>
//位段
struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
int main()
{printf("%d\n", sizeof(struct A)); //8return 0;
}

上述代码中,struct A中的成员都是 int 类型的,因此先开辟4个字节,先把前三个成员(17个比特位)存下来,还剩余了15个比特位,不够存储_d,于是再开辟4个字节,_d就能存下了! 于是最终结构体的大小就是8个字节, 32个比特位

问题是_d的空间如何分配,是先使用剩余的15个比特位,再使用15个比特位呢? 还是直接使用新开辟的4个字节(32个比特位)中的30个比特位呢?? 答案是 不确定!

2. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。


#include <stdio.h>
//位段
struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};
int main()
{struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;printf("%d\n", sizeof(s)); //3return 0;
}

假设:

1.每一个字节的空间存放时从右向左存放

2.当这1个字节不够下一个位段成员存储时就从下一个字节开始存

根据上面两点假设,得到如下结果:

 经过vs2022调试观察,发现vs2022的位段存储就是基于上面两点假设

位段的跨平台问题

1. int 位段被当成有符号数还是无符号数是不确定的。

2. 位段中最大位的数目不能确定。

(比如 int 整数, 16位机器最大16,32位机器最大32,写成27,在16位机器会出问题)

3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

总结:

跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

位段的应用

网络中常用到,后期网络部分的博客会有介绍

枚举

枚举类型的定义

enum Day//星期
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};enum Sex//性别
{MALE,FEMALE,SECRET
};enum Color//颜色
{RED,GREEN,BLUE
};

枚举类型中包含的成员就是一个个常量,叫做枚举常量,枚举常量是有取值的,默认从0开始,依次递增一

#include <stdio.h>
enum Day//星期
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};
int main()
{printf("%d\n", Mon); //0printf("%d\n", Tues); //1printf("%d\n", Wed); //2 printf("%d\n", Thur); //3printf("%d\n", Fri); //4printf("%d\n", Sat); //5printf("%d\n", Sun); //6return 0;
}

当然我们在定义枚举变量的时候可以赋初值,从被赋初值的枚举常量开始往后的值都是递增1

#include <stdio.h>
enum Day//星期
{Mon,Tues,Wed = 3,Thur,Fri,Sat,Sun
};
int main()
{enum Day d = Fri;printf("%d\n", Mon); //0printf("%d\n", Tues); //1printf("%d\n", Wed); //3printf("%d\n", Thur); //4printf("%d\n", Fri); //5printf("%d\n", Sat); //6printf("%d\n", Sun); //7return 0;
}

枚举的优点

1. 增加代码的可读性和可维护性

比如switch - case 进行分支判定时,可以用枚举常量代替0,1, 2等数字,可以很直观的看出某个分支的含义!
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。

C语言对类型的检查不是很严格,但是C++对类型的检查更加严格,

#include <stdio.h>
enum Day//星期
{Mon,Tues,Wed = 3,Thur,Fri,Sat,Sun
};
int main()
{enum Day d = 5; //errreturn 0;
}

3. 防止了命名污染(封装)
4. 便于调试

#define定义的标识符和宏都是在编译阶段就完成替换的,而调试是将代码已经编译成了二进制程序,此时都完成了替换,调试起来的代码和最开始的就不一样了!
5. 使用方便,一次可以定义多个常量

联合体(共用体)

联合类型的定义

联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)

#include <stdio.h>
//定义一个联合体类型
union Un
{char c;int i;
};
int main()
{union Un u; //定义一个联合体变量printf("%d\n", sizeof(u)); //4printf("%p\n", &u);  //00AFFB78printf("%p\n", &u.c); //00AFFB78printf("%p\n", &u.i); //00AFFB78return 0;
}

注:取地址永远取出的是最低一个字节的地址

联合体的特点

● 由于联合成员公用一块空间,因此同一时刻只能使用其中一个联合成员

● 对一个联合成员的修改可能会影响另一个联合成员

联合体的大小

● 联合的大小至少是最大成员的大小

● 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

● 对齐数 = 编译器的默认对齐数 和 联合体成员大小 的较小值

#include <stdio.h>
union Un1
{char arr[5];int i;
};
int main()
{printf("%d\n", sizeof(union Un1)); //8return 0;
}

联合体的大小至少是最大成员的大小,也就是数组大小是5,arr虽然是数组,但其实被看成一个一个的char,  所以对齐数是1,i 的对齐数是4,所以最大对齐数就是4,因此最终联合体的大小不是5,应该是8

联合体的应用

判断机器大小端

●大端: 数据的高字节保存在内存的低地址中, 数据的低字节保存在内存的高地址中

●小端: 数据的低字节保存在内存的低地址中, 数据的高字节保存在内存的高地址中

#include <stdio.h>
int check_sys()
{union Un{char c;int i;}u;u.i = 1;//小端: 01 00 00 00//大端: 00 00 00 01return u.c;
}
int main()
{int ret = check_sys();if (ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}

http://www.yayakq.cn/news/812308/

相关文章:

  • 网站建设公司成就丰县微网站开发
  • 玫琳凯网站建设与推广方案高性能网站建设进阶...
  • 网站制作公司兴田德润怎么联系团员个人信息查询官网
  • php做网站毕设答辩问什么seo伪原创工具
  • 实验室网站建设网站后台费用
  • wordpress仪表盘404哈尔滨网站优化技术
  • 怎么下学做衣服网站广东建筑人才网
  • 网站框架是怎么做的长沙网站建设icp备
  • 有了域名怎么做自己得网站网站建设有何好处
  • 全网营销网站建设临沂建设规划局网站
  • 网站是怎么做长沙网站优化体验
  • 郴州文明网网站磁力蜘蛛种子搜索
  • 网站客户评价太原百度关键词搜索
  • 淘宝客代理网站怎么做个人做外贸怎么做
  • 能够做渗透的网站wordpress 无法保存
  • 网站开发课程设计参考文献莆田有建设网站的公司码
  • 移动端网站建设 新闻动态wordpress官方主题
  • 网站设计英文翻译辽宁省建设信息网
  • 大城县建设局网站深圳家具网站建设
  • 网站建设有关图片qq飞车哪个公司开发的
  • 宁夏建设厅网站公示确实网站的建设目标
  • 建设网站盈利2015黄冈做网站价格
  • 中国建设银行青海分行网站唐山做企业网站的公司
  • 凡科网站手机投票怎么做码制作官网
  • 网站定制 天津泉州seo计费管理
  • 注册网站后邮箱收到邮件旅游网站建设分析
  • 建设模板网站wordpress 发邮件设置
  • 网站优化前景手机做wifi中继上外国网站
  • 哪个公司做网站建设好我的主页
  • 查重网站开发建设网站的申请