C语言指定初始化器解析及其应用

由于笔者能力有限,文中如果出现错误的地方,欢迎大家给我指出来,我将不胜感激,谢谢~

#指定初始化器的概念

C90 标准要求初始化程序中的元素以固定的顺序出现,与要初始化的数组或结构体中的元素顺序相同。但是在新标准 C99 中,增加了一个新的特性:指定初始化器。利用该特性可以初始化指定的数组或者结构体元素。

#数组的指定初始化器

##一维数组的指定初始化器

利用指定初始化器的特性,我们可以这样定义并初始化一个数组:

int a[6] = {[4] = 10,[2] = 25};

上述的初始化就等同于如下方式:

int a[6] = {0,0,25,0,10,0};

可以看到通过这种方式能够不按照顺序,且指定具体的元素进行初始化。除了上述这样的用法,我们也能够初始化数组内一段范围内的用元素,比如这样:

int a[5] = {[4] = 10,[0 ... 3] = 23};

上面这段程序的初始化也就等同于如下初始化:

int a[5] = {23,23,23,23,10};

那如果数组初始化里有指定的元素初始化又有未指定的元素又是如何分析呢?比如这样:

int a[5] = {11,[3] = 44,55,[1] = 22,33};

那它等同于下面的代码:

int a[5] = {11,22,33,44,55};

如果定义数组时没有指定数组的大小,那么数组实际的大小又是多少呢?比如这样:

int main(void)
{int number[] = {[20] = 1,[10] = 8,9};int n = sizeof(number)/sizeof(number[0]);printf("The Value of n is:%d\n",n);
}

输出结果是这样的:

The Value of n is:21

也就是说,如果未给出数组的大小,则最大的初始化位置确定数组的大小

#二维数组的指定初始化器

二维数组同样可以采用指定初始化器的方法,下面是一个二维数组的初始化:

int array[2][2] = 
{[0] = {[0] = 11},[1] = {[1] = 22},
};

这样的初始化也就等同于下述代码:

int array1[2][2] = 
{{11,00},{00,22}
};

通过上述代码,我们也可以知道,二维数组的指定初始化器的方法中,第一个 []里的数字表示的是初始化的二维数组的行数,而在 {}内的则是对当前行的元素进行初始化,实际也就是说 {}内的初始化方法也就和一维数组的一样了,一维数组可行的方法,二维数组也是可行的。

#应用

在讲述了数组指定初始化器的基本概念之后,我们来看一个具体的例子,下面这个例子是基于状态机的编程方法实现的 ATM 机器,首先 ATM 具有如下几种状态;

我们就可以使用状态机的思路来编写这个程序,首先使用枚举的方式来定义各个状态和相应的操作:

typedef enum
{Idle_State,Card_Inserted_State,Pin_Entered_State,Option_Selected_State,Amount_Entered_State,last_State
}eSysyemState;
typedef enum
{Card_Insert_Event,Pin_Enter_Event,Option_Selection_Event,Amount_Enter_Event,Amount_Dispatch_Event,last_Event
}eSystemEvent;

然后是对应操作的具体实现:

eSysyemState AmountDispatchHandler(void)
{return Idle_State;
}
eSysyemState EnterAmountHandler(void)
{return Amount_Entered_State;
}
eSysyemState OptionSelectionHandler(void)
{return Option_Selected_State;
}
eSysyemState InsertCardHandle(void)
{return Card_Inserted_State;
}
eSysyemState EnterPinHandler(void)
{return Pin_Entered_State;   
}

为了使得状态机的实现看起来不是那么的冗长,我们这里采用查表的方式,首先重定义一个函数指针二维数组类型:

typedef eSysyemState (* const afEventHandler[last_State][last_Event])(void);

简单说一个这是一个二维数组,二维数组里面存放的是函数指针,这个函数指针指向的是返回值为 eSysyemState,形参为 void 的函数。在重定义了这个类型之后,我们就可以用其定义新的变量了,在这之前,补充一点数组相关的内容,比如有如下代码:

typedef int array[3];
array data;

那么上述代码也就等同于如下代码:

int data[3];

有了上述代码之后,我们就可以实现我们的查找表了,具体代码如下:

    static afEventHandler StateMachine = {[Idle_State] = {[Card_Insert_Event] = InsertCardHandle},[Card_Inserted_State] = {[Pin_Enter_Event] = EnterPinHandler },[Pin_Entered_State] = {[Option_Selection_Event] = OptionSelectionHandler},[Option_Selected_State] = {[Amount_Entered_Event] = EnterAmountHandler},[Amount_Entered_State] = {[Amount_Dispatch_Event] = AmountDispatchHandler},};

现在再来看到这个初始化的方法也就比较清楚了,这实际上也就是一个二维数组使用指定初始化器解析的方法,最后,也就是我们的状态机运行代码:

#include <stdio.h>
int main(void)
{eSysyemState eNextState = Idle_State;eSystemEvent eNewEvent;while(1){eNewEvent = ReadEvent();/*省略相关判断*/eNextState = (*StateMachine[eNextState][eNewEvent])();}return 0;
}

#结构体的指定初始化器

定义了如下结构体:

struct point
{int x,y;
}

那么对于结构体变量的初始化可以采用以下的方式:

struct point p = 
{.y = 2,.x = 3
};

上述代码也就等价于如下代码:

struct point p = {3,2};

那这样的初始化有什么作用呢?下面是 linux 内核的一段代码:

const struct file_operations eeprom_fops =
{.llseek  = eeprom_lseek,.read    = eeprom_read,.write   = eeprom_write,.open    = eeprom_open,.release = eeprom_close
};

上述就是通过指定初始化器的方法来进行初始化的,其中 file_operations 这个结构体中的成员有很多,上述初始化的成员只是其中一部分,

struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);/*还有很多,省略*/}

采用这种指定初始化器的方法,使用灵活,而且代码易于维护。因为如果按照固定顺序赋值,当我们的 file_operations 结构体类型发生改变时,比如添加成员、减少成员、调整成员顺序,那么使用该结构体类型定义变量的大量 C 文件都需要重新调整初始化顺序,那将导致程序大幅度地更改。

#结构体数组的指定初始化器

在叙述了上面关于结构体和数组的指定初始化器之后,我们也可以以这种方式来来初始化结构体数组,比如这样:

#include <stdio.h>
int main(void)
{struct point {int x,y;};struct point pts[5] ={[2].y = 5,[2].x = 6,[0].x = 2};int i;for(i = 0;i < 5;i++)printf("%d %d\n",pts[i].x,pts[i].y);
}

输出结果如下:

2 0
0 0
6 5
0 0
0 0

#总结

以上便是指定初始化器所包含的大致内容,这也是自己之前的知识盲点,通过这次总结学习,也能够很好的掌握了,不积跬步,无以至千里~

参考资料:

[1] https://blog.51cto.com/zhaixue/2346825 [2] https://www.geeksforgeeks.org/designated-initializers-c/ [3] https://aticleworld.com/state-machine-using-c/

您的阅读是对我最大的鼓励,您的建议是对我最大的提升

  回复「 篮球的大肚子」进入技术群聊

回复「1024」获取1000G学习资料

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/467711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

递归神经网络

文章目录LSTM![在这里插入图片描述](https://img-blog.csdnimg.cn/20200609171449198.pngLSTM 4INPUTS 1outputs

CNN+RNN

文章目录相同点不同点组合意义组合方式图片标注基本思路模型设计&#xff0d;数据准备视频行为识别视频行为识别图片问答相同点 传统神经网络的扩展 前向计算产生结果&#xff0c;反向计算模型更新 每层神经网络横向可以多个神经元共存&#xff0c;纵向可以多层神经网络链接 …

深入理解Linux内核链表

之前写过的链表文章&#xff0c;再结合这篇&#xff0c;我觉得是一道硬菜。Linux内核链表C语言&#xff0c;链表大家五一节日快乐&#xff0c;我知道劳动节大家都辛苦了&#xff0c;吃点硬菜好顶住饿肚子~#一、 链表数据结构简介链表是一种常用的组织有序数据的数据结构&#x…

GAN

文章目录生成对抗网络(GAN)基础生成对抗网络 优点&#xff1a;缺点&#xff1a;生成对抗网络深度GANDCGAN结构细节特征研究DCGAN总结条件GAN模型结构INFOGANInfoGAN: 自动学习z中部分变量意义Wasserstein GANGAN存在问题原因WGAN特点改进方法&#xff1a;生成对抗网络(GAN)基础…

1200可以读取modbus tcp_S7-1200 作 MODBUS TCP服务器

S7-1200 Modbus TCP 通信指令块STEP 7 V13 SP1 软件版本中的Modbus TCP库指令目前最新的版本已升至V4.0&#xff0c;该版本的使用需要具备以下两个条件&#xff1a;1. 软件版本&#xff1a; STEP 7 V13 SP1及其以上2. 固件版本&#xff1a; S7-1200 CPU 的固件版本V4.1图1. Mod…

Linux一定需要文件系统吗?

开篇题外话&#xff1a;对于Linux初学者来说&#xff0c;这是一个很纠结的问题&#xff0c;但这也是一个很关键的问题&#xff01; 一语破天机&#xff1a;“尽管内核是 Linux 的核心&#xff0c;但文件却是用户与操作系统交互所采用的主要工具。这对 Linux 来说尤其如…

迁移学习

文章目录为什么需要迁移学习模型Fine-‐tune![在这里插入图片描述](https://img-blog.csdnimg.cn/20200612000845217.png)保守训练层迁移Multitask Learning渐进式神经网络/Progressive Neural Networks域对抗零样本学习Self-‐taught learning为什么需要迁移学习 使用深度学习…

怎么撤回操作_微信又更新,拍一拍能撤回了

微信拍一拍功能在 6 月份刚上线时&#xff0c;「微信之父」张小龙就发朋友圈表示&#xff0c;微信史上仅需一行代码的有趣功能终于来了&#xff0c;拍一拍&#xff0c;像蚂蚁一样打招呼。拍一拍功能将大家在现实世界的肢体交流带到了虚拟世界&#xff0c;相信大家都有儿时和三俩…

吹牛

起了一个不太雅观的标题&#xff0c;我很想给我们的会晤起一个响亮的名字&#xff0c;比如「三方会谈」或者「新一代5.4事件」&#xff0c;再或者牛逼点的&#xff0c;我们可以叫「西乡起义」。好吧&#xff0c;我摊牌了&#xff0c;这次吃饭就是三个屌丝和一个大佬的闲暇吹牛&…

Spring MVC-视图解析器(View Resolverr)-内部资源视图解析器(Internal Resource View Resolver)示例(转载实践)...

以下内容翻译自&#xff1a;https://www.tutorialspoint.com/springmvc/springmvc_internalresourceviewresolver.htm 说明&#xff1a;示例基于Spring MVC 4.1.6。 InternalResourceViewResolver用于将提供的URI解析为实际的URI。以下示例显示如何使用Spring Web MVC框架使用I…

手机是怎么确定位置信息的?

#手机是怎么定位的&#xff1f;定位是一个老生常谈的话题&#xff0c;最近几年还在讨论一个热点技术话题「室内定位」&#xff0c;从我知道这个技术到现在已经过了好几年了&#xff0c;也出现了一些室内定位的方案&#xff0c;而我们的手机是如何进行定位的&#xff0c;又有哪些…

tablestore列式存储原理_10分钟搞透:技术人必会的MySQL体系结构与存储引擎!

MySQL是目前使用最广的开源数据库&#xff0c;不管从装机量、使用人群、专职人员、社区发展&#xff0c;还是基于MySQL的其他分支&#xff0c;都是当之无愧的No.1。 本文将从以下4个方面&#xff0c;带你搞透MySQL体系结构与存储引擎。主要包括&#xff1a;1、MySQL数据库的体系…

使用Adobe Audition生成基本音频

#首先打开软件 #新建一个音频 #在效果菜单栏插入基本音频

更多网络类型

文章目录丰富网络类型CPPN孪生网络Triplet Network应用Variational Auto-encoder强化学习Markov decision processesBellman公式丰富网络类型 深度学习除了经典卷积神经网络&#xff0c;循环神经网络还有广泛的网络类型 CPPN 网络输入是像素坐标值&#xff08;x&#xff0c;…

更多框架

文章目录关于框架Caffe基于层的设计思路Protocol Buffer 技术prototxt .caffemodel文件caffe的训练主要特点TorchLUA语言主要特点Tensorflowcomputation graphsMXNET关于框架 Caffe 依赖大量第三方库 为了读取图像&#xff0c;以及简单的图像处理&#xff0c;连接很重的Openc…

.net的label的背景如何设置成为透明_css如何设置背景图片?background属性添加背景图片...

在前端开发过程中&#xff0c;为了页面的美观&#xff0c;往往都会给html页面添加背景图片。那么如何利用css设置html中用图片做背景&#xff1f;本章就给大家介绍css怎样设置背景图片。有一定的参考价值&#xff0c;有需要的朋友可以参考一下&#xff0c;希望对你有所帮助。程…

苹果uwb定位技术

昨天的文章简单说明了手机定位的技术&#xff0c;文章写的比较简单&#xff0c;但是阅读量却还可以&#xff0c;这篇文章转一个uwb定位技术的文章&#xff0c;让更多的人了解这项技术。相关阅读&#xff1a;手机是怎么确定位置信息的&#xff1f;载波和LoRa#前言关于昨天的文章…

springboot导包显示不存在_(一)SpringBoot搭建基本后端应用

boot在计算机术语中是启动的意思&#xff0c;SpringBoot也就是Spring的启动器。稍有经验的JavaWeb程序员对于传统SSM结构的MVC应用&#xff0c;大多数最不好的体验就是搭建一个项目需要进行大量的配置。稍有不慎就可能采坑。更关键的是有些配置基本就不会去进行定制化修改。为了…

f12获取网页文本_8招教你快速搞定网页内容禁止复制粘贴,想怎么复制就怎么复制...

大家平时在搜索资料、浏览网页时&#xff0c;经常会复制一些内容。尤其是文字比较多时&#xff0c;比起一个个字手打&#xff0c;复制能省下不少功夫。可有时候好不容易找到资料了&#xff0c;却发现有些网站上的内容文本复制不了&#xff1f;甚至右键菜单都打不开&#xff01;…

为什么需要超过48k的采样音频?

最近在看音频的事情&#xff0c;随便拿点东西出来聊一下&#xff0c;如果说的不对&#xff0c;请用棒槌来打我&#xff0c;这样我晚上睡觉就不用数绵羊了。我播放一个20HZ~20KHZ的音频&#xff0c;如下图我使用16K的采样率来采集它是声音信号&#xff0c;获取音频如下图我使用4…