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

#指定初始化器的概念

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学习资料

C语言指定初始化器解析及其应用相关推荐

  1. 【C语言笔记】指定初始化器

    C99增加了一个新特性:指定初始化器(designated initializer).利用该特性可以初始化指定的数组元素,也可以初始化指定的结构体变量. 本文主要分享:使用指定初始化器初始化数组. 例 ...

  2. C语言数组-指定初始化器(C99)

    指定初始化器(designated initializer):利用 该特性可以初始化指定的数组元素:例如,只初始化数组中的最后一个元素. 即: int arr[6]={[5]=3};          ...

  3. C语言数组指定初始化器

    C99增加了一个新特性:指定初始化器.利用该特性可以初始化指定的数组元素.例如,只初始化数组中的最后一个元素.对于传统的的C初始化语法,必须初始化最有一个元素之前的所有与还俗,才能初始化它: int ...

  4. 6.Swift协议|扩展|访问权限|异常调试|类型转换|运算函数|ARC|类类型初试化器|值类型初始化器...

    1. 协议(Protocol):与OC之间唯一不同的是Swift中的协议不管是属性还时方法全部是必须实现的 /** protocol*/ protocol FullNamed { /** 计算属性申明 ...

  5. C语言面试干货——指定结构体初始化器(GCC手册解析)

    文章目录 指定结构体初始化器(Designated Initializers) 背景 初始化struct 初始化union 对于重复初始化的说名 本人就职于国际知名终端厂商,负责modem芯片研发. ...

  6. 小笔记:表 - 各种语言的 CommonMark Markdown解析器 实现

    表:各种语言的 CommonMark Markdown解析器 实现 本文地址:https://blog.csdn.net/qq_28550263/article/details/128735962 1 ...

  7. C语言结构体——指定初始化

    概述 C 语言结构体指定初始化(Designated Initializer)实现上有两种方式: 一种是通过点号加赋值符号实现,即.fieldname = value, 另一种是通过冒号实现,即fie ...

  8. 【Kotlin】属性 与 幕后字段 ( 属性声明 | 属性初始化器 | 属性访问器 | field 属性幕后字段 | lateinit 延迟初始化属性 )

    文章目录 I . 属性 字段 总结 II . 属性声明 III . 属性初始化器 IV . get / set 属性访问器 V . 属性幕后字段 field VI . 变量和常量的区别 VII . 延 ...

  9. 第七节:语法总结(1)(自动属性、out参数、对象初始化器、var和dynamic等)

    一. 语法糖简介   语法糖也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方 ...

最新文章

  1. 分布式架构的对比-IBM XIV
  2. 【Netty】ChannelHandler和ChannelPipeline
  3. 炼一项专业技能c语言,C语言程序设计_安徽新华电脑专修学院
  4. godep的save和update
  5. 钢铁侠机器人公仔_这个618,买个钢铁侠机器人和儿子一起回童年
  6. 输出注册信息javabean
  7. 网络上经典的DOS小命令
  8. 【渝粤题库】陕西师范大学720001 分子生物学
  9. mysql大量数据合并_mysql中将多行数据合并成一行数据
  10. Python学习day07 - Python进阶(1) 内置方法
  11. 【flume】flume读取web应用某个文件夹下日志到hdfs
  12. 时间序列多步预测_使用LSTM深度学习模型进行温度的时间序列单步和多步预测...
  13. Google 纪念万维网 30 年:没有 HTTP 协议就没有互联网
  14. 什么样的公司值得加入?
  15. 《Advanced .NET Debugging》 读书笔记 Listing 5-6: Pining的简单示例
  16. the first blog
  17. UE4使用贴花(Decal)
  18. 阿里云OSS服务器上传图片并获取路径(SpringBoot)(☆)
  19. 什么是用计算机的主存,计算机的主存储器是指什么
  20. 2021-11-15----韩顺平Java入门第九天

热门文章

  1. 为ESXI 添加ISCSI存储设备 Linux服务器系统
  2. 腾讯2016春招安全岗笔试题解析
  3. 在JS函数中执行C#中的函数、字段
  4. 了解 Windows Azure 存储的可伸缩性、可用性、持久性和计费
  5. java动态代理二cglib
  6. _beginthreadex 一定要自己写 CloseHandle 可以不用 _endthreadex
  7. 编写优美的GTest测试案例
  8. VLAN 路由实验图解
  9. 50ETF期权波动率策略
  10. 【笔试记录】2021/3/13美团