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

天河电子商务网站建设福建seo排名培训

天河电子商务网站建设,福建seo排名培训,做自行车车队网站的名字大全,网络营销学校哪个比较好目录 结构体 结构体类型的声明 匿名结构体 结构的自引用 结构体变量的定义和初始化 结构体成员变量的访问 结构体内存对齐 结构体传参 位段 位段类型的声明 位段的内存分配 位段的跨平台问题 位段的应用 枚举 枚举类型的定义 枚举的优点 联合体(共用体) 联合…

目录

结构体

结构体类型的声明

匿名结构体

结构的自引用

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

结构体成员变量的访问

结构体内存对齐

结构体传参

位段

位段类型的声明

位段的内存分配

位段的跨平台问题

位段的应用

枚举

枚举类型的定义

枚举的优点

联合体(共用体)

联合类型的定义

联合体的特点

联合体的大小

联合体的应用


结构体

结构体类型的声明

之前学习到的数据类型我们称之为内置类型,如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.yidumall.com/news/44611.html

相关文章:

  • 无锡做设计公司网站市场营销课程
  • 河南网站建设软件微博营销策略
  • 建设营销网站的四个步骤常德论坛网站
  • 标准型网站建设成人大学报名官网入口
  • 红旗网站建设广告联盟骗局
  • 网站设计集团软文广告范例大全
  • 什么网站做app好百度推广方式有哪些
  • 如何把国外的网站在国内做镜像网站备案查询系统
  • 昌乐网站建设温州seo
  • 社区教育网站建设方案我们seo
  • 花都有沒有网站建设的seo公司哪家好
  • 网站开发体会怎么推广自己的店铺
  • 合肥网站seo报价赚钱平台
  • 常见的营销型网站百度一键优化
  • 常见的网站推广方法seo是什么?
  • 长春市网站开发成长电影在线观看免费
  • flash网站源码免费下载白酒营销策划方案
  • 十堰网站搜索优化价格营销策划公司 品牌策划公司
  • 太原h5建站关键词优化怎么做
  • 武汉网站开发公司哪家好南宁seo多少钱报价
  • 哪个网站可以做链接网站优化名词解释
  • 合肥做一个网站要多少钱手机优化软件哪个好
  • 顾客评价网站鄂州网站seo
  • 三星官网网站广州品牌seo推广
  • 网站开发人员没有按照设计开发百度一下网页版搜索引擎
  • 专业积分商城网站建设徐州seo代理计费
  • 成功卡耐基网站建设无锡百度正规公司
  • 长沙seo排名扣费站长seo查询工具
  • win7环境wordpress正规的关键词优化软件
  • 网站源码多少钱东莞营销网站建设