桃猿三结义:结构、枚举、联合 上

简介: 桃猿三结义:结构、枚举、联合

文章目录

前言

古有刘备、关羽、张飞桃园三结义

现有结构、枚举、联合桃猿三结义

一、结构体

💦 什么是结构体

🔑 官方来说结构体就是一些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。说到集合,数组也是集合,但是不同的是数组只能是相同类型元素的集合

💦 结构体类型的声明

------------------------结构体声明样式------------------------

struct tag

{

  member1;

  member2;

} variable-list;

------------------------解释------------------------

▶ struct是结构体关键字

▶ tag是结构体的标签名,是自定义的

▶ struct tag就是结构体类型

▶ {}里面放的是成员列表

▶ variable-list是变量

struct Book
{
  char name[20];
  int price;
  char id[];  
}b4, b5, b6;//2、创建结构体变量
int main()
{
  //1、创建结构体变量
  struct Book b1;
  struct Book b2;
  struct Book b3;
  return 0;
}

📝 分析:

这里定义了一个结构体类型struct Book,再使用类型创建变量 (两种方法创建结构体变量):

相同的是:

▶ 它们的类型是相同的,都是struct Book

不同的是:

▶ 在main函数内创建的变量b1,b2,b3是局部变量

▶ 在main函数外创建的变量b4,b5,b6是全局变量


------------------------特殊的结构体声明样式------------------------

struct

{

  char a;

  int b;

  double c;

} s;

------------------------解释------------------------

▶ 这个结构体没有标签名tag

▶ 在声明结构体的时候,可以不完全声明

▶ 这种类型的结构体叫做匿名结构体

▶ 使用匿名结构体直接创建变量s

struct 
{
  char a;
  int b;
  double c;
} s;
struct 
{
  char a;
  int b;
  double c;
} *ps;//使用匿名结构体类型创建一个变量,这个变量是一个指针
int main()
{
  ps = &s;//?
  return 0;
}

❓❔ 两个相同的匿名结构体类型去创建变量 s 和 *ps ,问这里的 ps = &s; 是合法的吗

📐 验证:

📝分析:

▶ 在编译器看来,虽然结构体的成员是一样的,但是它会认为 s 和 *ps 是两个不同的类型,所以是非法的

▶ 所以可以试想一下,使用匿名结构体去创建变量时,只能用一次

💦 结构体的自引用

❓❔ 结构体成员包含该结构体创建的结构体变量(非指针)

struct N
{
  int a;
  struct N n;//?
};
int main()
{
  struct N n;
  return 0;
}

📝 分析:

假设这种写法是可行的,那么使用struct N去创建一个变量n,请问n的大小是多大?细想一下,你搁这卡bug呢? 这不是无限套娃吗?😵😵

📐 验证:

语法都不支持

✔ 但是结构体成员可以包含其它结构体创建的结构体变量(嵌套结构体)

struct U
{
  int b;
  int c;
};
struct N
{
  int a;
  struct U n;//?
};
int main()
{
  struct N n;
  return 0;
}

🧿 拓展


❓❔ 什么是数据结构

数据结构指的是数据在内存中存储结构


🧷 举例:如果要存储1 2 3 4 5

🔎 这里主要了解链表:

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

🎗 创建局部/全局结构体变量并初始化

struct Book
{
  char name[20];
  int price;
  char id[];  
}b4, b5, b6;
int main()
{
  //使用局部变量初始化
  struct Book b1 = { "CSDN", 38, "202306030033" };
  //使用全局变量初始化
  struct Book b4 = { "C语言", 40, "2451176292" };
  return 0;
}

🎗 嵌套结构体的初始化和成员访问

struct Stu
{
  char name[20];
  int age;
};
struct Book
{
  char name[20];
  int price;
  char id[20];  
  struct Stu s;
};
int main()
{
  //初始化
  struct Book b = { "C语言结构体", 40, "133927471", { "小明", 20 } };
  //访问成员
  //使用.
  printf("%s %d %s\n%s %d\n", b.name, b.price, b.id, b.s.name, b.s.age);
  //使用->
  struct Book* ps = &b;//定义一个结构体类型的指针指向b的地址 
  printf("%s %d %s\n%s %d\n", ps -> name, ps -> price, ps -> id, ps -> s.name, ps -> s.age);
  return 0;
}

💦 结构体内存对齐

❓❔ 一个结构体占多大字节呢,是不是直接把一个结构体里每个成员的类型大小加起来呢

#include<stdio.h>
struct S  
{
  int i;
  char c;
};
int main()
{
  struct S s = { 0 };
  printf("%d\n", sizeof(s));
  return 0; 
}

❓❔ 假设结构体的大小 = 结构体里每个成员大小之和,那么这里的结果是5个字节

📐 验证:

✔ 说明对于结构体是如何计算大小有它自己的规则


⚠ 这个规则就是结构体内存对齐

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

▶ 其它成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。

 VS中默认对齐数是8; Linux没有默认对齐数,它是按照自身大小来对齐的

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

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


⚠ 为什么会存在内存对齐

注:大部分参考资料是这样说的(没有官方具体的说法):

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

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

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

人话:

▶ 假设某些硬件平台规定只能在4的倍数地址处去访问数据,那么未来在储存时就必须存储到指 定的位置上,这样才能被访问,所以在存储数据时最好能内存对齐

▶ 对于内存对齐:b的访问只需要一次;对于非内存对齐b的访问需要二次


❓❔ 在设计结构体时,如何做到既要满足对齐,又要节省空间

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

📝 小结:

s1和s2的类型成员一模一样,但是s1和s2的大小不同。发现让占用空间小的成员尽量集中在一起有助于节省空间


❓❔ 前面有说到VS的默认对齐数是8,能不能自己调整呢

#include<stdio.h>
//默认对齐数是8
struct S1 
{
  char c1;//0
  //1-3
  int i;//4-7
  char c2;//8
  //9-11
};
//修改默认对齐数为2
#pragma pack(2)//始
struct S2 
{
  char c1;//0
  //1
  int i;//2-5
  char c2;//6
  //7
};
#pragma pack()
int main()
{
  printf("%d\n", sizeof(struct S1));//12
  printf("%d\n", sizeof(struct S2));//8
  return 0;
}

📝 小结:

结构体在对齐方式不合适的时候,那么我们可以自己调整默认对齐数


相关文章
|
机器学习/深度学习 人工智能 算法
一文了解人工智能中常用的优化算法
优化算法包含很多种,如果按梯度类型进行划分,可以分为有梯度优化算法和无梯度优化算法,在大多数人工智能技术中常用有梯度优化算法,当然也会有些场景也会用到无梯度优化算法,比如在强化学习中会用到黑盒优化算法cma-es、贝叶斯优化等,有些时候也会用到遗传算法和粒子群优化算法。本文主要讲解机器学习\深度学习中一些常用的优化算法,梯度下降法、动量法momentum、Adagrad、RMSProp、Adadelta、Adam,介绍不同算法之间的关联和优缺点,后续会继续分享其他的算法,
一文了解人工智能中常用的优化算法
|
存储 缓存 算法
漫谈代码优化与效率提升
在当今快节奏的技术发展中,对于程序员来说,不仅仅是写出能运行的代码,更重要的是如何写出高效、优雅的代码,以提升工作效率和代码性能。本文从优化思路、技巧和实践三个方面探讨了代码优化与效率提升的方法,旨在为开发者提供一些实用的指导和启发。
494 31
|
算法 测试技术 编译器
蓝桥杯-02-python组考点与14届真题
蓝桥杯-02-python组考点与14届真题
|
网络协议
netmap: UDP 协议栈的实现
netmap: UDP 协议栈的实现
|
存储 算法 算法框架/工具
【opencv】计算机视觉:实时目标追踪
【opencv】计算机视觉:实时目标追踪
340 0
|
程序员 编译器 API
没有永远的王者…Zig替代C,将成定局!
没有永远的王者…Zig替代C,将成定局!
386 0
【笔记14】树的基本概念,二叉树,真二叉树,满二叉树,完全二叉树
节点、根节点、父节点、子节点、兄弟节点 空树:没有任何节点的树 一棵树可以只有 1 个节点(即只有根节点) 子树、左子树、右子树
509 0
【笔记14】树的基本概念,二叉树,真二叉树,满二叉树,完全二叉树
|
Web App开发 前端开发 JavaScript
用JS轻松实现一个录音、录像、录屏的工具库
哈喽,大家好,我是海怪。 最近项目遇到一个要在网页上录音的需求,在一波搜索后,发现了 react-media-recorder 这个库。今天就跟大家一起研究一下这个库的源码吧,从 0 到 1 来实现一个 React 的录音、录像和录屏的功能。
用JS轻松实现一个录音、录像、录屏的工具库
|
Dart 安全 IDE
Flutter Web实战项目打造真正跨平台应用(windows,android,ios,linux,macos,web)
Flutter Web项目 Flutter 最近发布了 Flutter V2.5.1,其性能得到了很大提升,支持 Web、macOS、Android 和 iOS。 这就是为什么今天我们使用在 Web、macOS 应用、Android 和 iOS 应用上运行的 flutter 创建响应式博客主题。 此外,我们创建了一个具有自定义悬停动画的动画网络菜单。 最后,您将学习如何使用 Flutter 制作响应式应用程序。
806 0
Flutter Web实战项目打造真正跨平台应用(windows,android,ios,linux,macos,web)
支付宝预授权配置芝麻分门槛、借用数量等信息流程分享
说明: 商户签约“支付宝预授权”接口成功,并且成功开通芝麻免押功能后就可以登录:[url]https://b.xin.xin/ant/index.htm[/url]   来自助配置芝麻分门槛、借用数量等信息。
1712 11