自定义类型:结构体,位段,枚举,联合体1

简介: 自定义类型:结构体,位段,枚举,联合体

😊前言😊

在初阶C语言中 ,我们已经接触过一些简单的结构体,让大家对结构体有了初步的了解,今天我们来深入研究一下结构体、枚举和联合体。希望大家会有多多少少的收获


🤔结构体🤔

结构体是什么?

结构体是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。


结构体声明

结构体定义需要用到关键字 struct 来定义 。

例如我们需要描述一个学生,就要有学生姓名、年龄、性别、学号等等。

我们试着把一个学生的信息用结构体表示出来>

struct Stu
{
  char name[20];//姓名
  int age;//年龄
  char sex[5];//性别
  char id[20];//学号
}s1,s2;//全局变量
int main()
{
  struct Stu s3, s4;//局部变量
  return 0;
}

也可以这样写>

typedef struct Stu
{
  char name[20];//姓名
  int age;//年龄
  char sex[5];//性别
  char id[20];//学号
}Stu;//typedef之后重定义的名称
Stu s1, s2;//全局变量s1,s2
int main()
{
  Stu s3, s4;//局部变量s3,s4
  return 0;
}


typedef 重定义将结构体名称定义为Stu,这样一来我们创建结构体变量就会方便很多了,直接Stu后面加上变量名称,就可以很好的创建出一个结构体变量。


匿名结构体

匿名结构体,顾名思义就是没有名字,他必须在定义的时候完成变量的创建,而且只能使用这一次,后面无法再次创建新的变量。

struct //不写标签
{
  int arr[10];
  int size;
}str;//str是这个结构体的变量


当然这种结构体我们用的会比较少,一般是这个结构体只是自己用而且只用一次的情况下,我们可以考虑使用匿名结构体。


结构体的自引用

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

这里我们先给大家介绍一下链表的结构:


f5b3faa999144b1db4a6f91791aeff79.png

链表的每一个节点有两个成员,一个是数值域data,另一个存放的是可以指向链表下一个节点的指针域。

那么了解了链表的这种结构,我们该如何去定义这种结构体呢?

先来看看这样子定义行不行?


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

结构体里面有一个data,还有下一个结构体,ok很完美,但是回头我们再来想一想


sizeof(struct Node)是多少呢? 一个int是四个字节,在一个struct Node类型的next,里面又包含了一个int类型和一个struct Node,这样无限循环下去,无限套娃,它所占的字节数也是无限大的。


显然这样定义是不可行的。


那我们试试另一种定义方式>

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

一个结构体里面一个data,还有一个指向下一个结构体类型的结构体指针。

这样一来他所占的字节数就是可控的了。这种定义方式就是结构体的自引用的正确方式。

那么如何使用typedef重命名呢?


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


切忌:不能将结构体中的struct Node*next写成Node*next;

结构体变量的初始化


struct Point
{
  int x;
  int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = {1,2};
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;
};
struct s2
{
  char c1;
  char c2;
  int i;
};
int main()
{
  printf("%d\n", sizeof(struct s1));
  printf("%d\n", sizeof(struct s2));
  return 0;
}

s1和s2里面的变量个数相同 ,那么好他们两个所占的字节数也一定一样咯。我们来运行看看>


41904bb8f717483b8a4e89ad22acc964.png


咦为什么一个是12一个是8呢?他俩明明变量都一模一样,只不过就是顺序不一样而已,为什么所占的字节数就不一样了呢。而且就算把他们里面的变量单独加起来也就6个字节,那12和8是怎么来的呢?

到这里就不得不提出结构体内存对齐了。那又该怎样去对齐呢?我们一起来研究一下>



c7dccc11ba8c4caf94d5490385a57e01.png





这样我们就可以理解为什么s1会是12个字节了。我们再来看看s2>



5574a1771e2f4b8c8b4c83e3d3d6af0b.png


我们再来练习一道s3>

struct S3
{
  double d;
  char c;
  int i;
};


69c885dc6756458f8791095c64606105.png


答案是不是16呢我们来验证一下>


b91ce6f4665b4bf39a03863cab6be203.png



学到这里,反过来问一下,内存中国为啥那么会存在对齐呢?他又有什么意义呢?


对于这个问题大部分参考资料是这么说的:


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

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


2. 性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。

原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。


总体来说:


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


那我们在设计结构体的时候,我们既要满足对齐,又要节省空间要怎么做到呢?


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


就好比如上面的s1、s2他们的成员都是一样的顺序不同他们之间所占字节就相差了4字节。


那这个默认对齐数可以修改吗还是只能是编译器自己规定的呢? 答案是可以修改


相关文章
|
存储 自然语言处理 算法
【编译原理】LR(1)分析法:C/C++实现
【编译原理】LR(1)分析法:C/C++实现
1918 0
|
存储 安全 Linux
Docker 离线安装与基本使用
Docker 离线安装与基本使用
4090 0
Docker 离线安装与基本使用
|
消息中间件 SQL JSON
阿里云物联网平台 “物模型属性” 的分析&&易错点&&上报属性时Payload如何正确组装?
您是否在纠结设备上报了数据,平台到底有没有收到? 您是否很疑惑物模型属性怎么老是不刷新? 您是否不理解物模型属性下发总是不生效? 您是否不知道上报属性时Payload到底该怎么填? 您是否很纳闷物模型属性一会又携带有时间戳,一会又没有? 您是否怀疑能不能自定义物模型属性的时间戳?又如何取到自定义时间戳? 您是否...
9465 3
阿里云物联网平台 “物模型属性” 的分析&&易错点&&上报属性时Payload如何正确组装?
|
10月前
|
运维 安全 Ubuntu
补丁别靠吼,Linux补丁要自动化!从 openEuler 打通到全栈实践方案
补丁别靠吼,Linux补丁要自动化!从 openEuler 打通到全栈实践方案
665 154
|
7月前
|
安全 定位技术 数据中心
社交媒体账号安全如何通过IP查询工具检测异常登录?
IP在账号安全风控中通过提供“语境信息”,可有效识别异常登录。核心方法是将IP的地理位置、网络环境(家庭宽带 vs 数据中心)与用户行为基线对比,揪出三类风险:物理上不可能的地理跳跃、网络环境突变,以及同一IP的批量撞库行为。借助专业IP工具,平台能实时获取街道级精度定位、代理识别及多维度风险标签,从而精准触发二次验证或拦截。这种从“查地址”到“主动防御”的进化,能显著提升盗号拦截率。对个人而言,定期检查登录记录并开启双重验证,是防范密码泄露后的最后防线。
社交媒体账号安全如何通过IP查询工具检测异常登录?
|
数据采集 算法 数据安全/隐私保护
【硬件测试】基于FPGA的2FSK调制解调系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR
本文介绍了基于FPGA的2FSK调制解调系统,包含高斯信道、误码率统计模块及testbench。系统增加了ILA在线数据采集和VIO在线SNR设置模块,支持不同SNR下的硬件测试,并提供操作视频指导。理论部分涵盖频移键控(FSK)原理,包括相位连续与不连续FSK信号的特点及功率谱密度特性。Verilog代码实现了FSK调制解调的核心功能,支持在不同开发板上移植。硬件测试结果展示了不同SNR下的性能表现。
766 6
|
机器学习/深度学习 人工智能 计算机视觉
AI计算机视觉笔记二十三:PP-Humanseg训练及onnxruntime部署
本文介绍了如何训练并使用PaddleSeg的人像分割模型PP-HumanSeg,将其导出为ONNX格式,并使用onnxruntime进行部署。首先在AutoDL服务器上搭建环境并安装所需库,接着下载数据与模型,完成模型训练、评估和预测。最后,通过paddle2onnx工具将模型转换为ONNX格式,并编写预测脚本验证转换后的模型效果。此过程适用于希望在不同平台上部署人像分割应用的开发者。
1555 0
|
缓存 Java UED
仅执行一次的定时任务如何设置?
【10月更文挑战第12天】仅执行一次的定时任务如何设置?
1503 1
|
存储 安全 算法
从系统复杂性看软件架构
一、架构设计是为了解决系统复杂性整个软件技术发展的历史,其实就是一部与“复杂性”斗争的历史。架构也是为了应对软件系统复杂性而提出的一个解决方案,其主要目的是为了解决软件系统复杂性带来的问题。这里包括两个名词:系统和复杂性,下面分别对其进行解析1.1 复杂性的定义复杂性这个名词很复杂,麻省理工学院的物理学家塞思·劳埃德统计了复杂性的定义数量,至少有45种:信息 ,熵 ,算法复杂性 ,算法信息量 ,费
11258 2
从系统复杂性看软件架构
|
存储 编解码 搜索推荐
如何在Windows和Mac上免费将蓝光转换为MKV?
蓝光光盘因能提供高质量的视频和音频内容而备受青睐,但其使用上的局限性却不容忽视。相比之下,MKV作为一种广受支持的视频格式,与大多数播放设备和平台都能完美兼容,为用户带来了更大的便利性和灵活性。
2847 0

热门文章

最新文章