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

简介:

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

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

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

Copy
int a[6] = {[4] = 10,[2] = 25};
上述的初始化就等同于如下方式:

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

Copy
int a[5] = {[4] = 10,[0 ... 3] = 23};
上面这段程序的初始化也就等同于如下初始化:

Copy
int a[5] = {23,23,23,23,10};
那如果数组初始化里有指定的元素初始化又有未指定的元素又是如何分析呢?比如这样:

Copy
int a[5] = {11,[3] = 44,55,[1] = 22,33};
那它等同于下面的代码:

Copy
int a[5] = {11,22,33,44,55};
如果定义数组时没有指定数组的大小,那么数组实际的大小又是多少呢?比如这样:

Copy
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);

}
输出结果是这样的:

Copy
The Value of n is:21
也就是说,如果未给出数组的大小,则最大的初始化位置确定数组的大小

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

Copy
int array2 =
{

[0] = {[0] = 11},
[1] = {[1] = 22},

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

Copy
int array12 =
{

{11,00},
{00,22}

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

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

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

Copy
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;
然后是对应操作的具体实现:

Copy
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;   

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

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

Copy
typedef int array[3];
array data;
那么上述代码也就等同于如下代码:

Copy
int data[3];
有了上述代码之后,我们就可以实现我们的查找表了,具体代码如下:

Copy

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},
};

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

Copy

include

int main(void)
{

eSysyemState eNextState = Idle_State;
eSystemEvent eNewEvent;
while(1)
{
    eNewEvent = ReadEvent();
    /*省略相关判断*/
    eNextState = (*StateMachine[eNextState][eNewEvent])();
}
return 0;

}
结构体的指定初始化器#
定义了如下结构体:

Copy
struct point
{

int x,y;

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

Copy
struct point p =
{

.y = 2,
.x = 3

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

Copy
struct point p = {3,2};
那这样的初始化有什么作用呢?下面是 linux 内核的一段代码:

Copy
const struct file_operations eeprom_fops =
{
.llseek = eeprom_lseek,
.read = eeprom_read,
.write = eeprom_write,
.open = eeprom_open,
.release = eeprom_close
};
上述就是通过指定初始化器的方法来进行初始化的,其中 file_operations 这个结构体中的成员有很多,上述初始化的成员只是其中一部分,

Copy
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 文件都需要重新调整初始化顺序,那将导致程序大幅度地更改。

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

Copy

include

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);

}
输出结果如下:

Copy
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/

作者: wenzid

出处:https://www.cnblogs.com/wenziw5/p/12792740.html

相关文章
|
1天前
|
Linux 编译器 调度
xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务
本文介绍了如何将POSIX应用程序编译为在Xenomai实时内核上运行的程序。
15 1
xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务
|
1天前
|
存储 Rust 监控
EtherCAT主站IgH解析(一)--主站初始化、状态机与EtherCAT报文
本文介绍了IgH EtherCAT Master整体运行原理
5 0
EtherCAT主站IgH解析(一)--主站初始化、状态机与EtherCAT报文
|
3天前
|
供应链 搜索推荐 API
API在电子商务中的应用与优势:深入解析
API是电子商务成功的关键,它们不仅促进了技术创新,还提高了用户体验和运营效率。随着技术的不断进步,API将继续在电子商务领域发挥更加重要的作用。电子商务平台通过利用API,可以更加灵活地适应市场变化,提供更加丰富和个性化的购物体验,最终实现业务的增长和扩展。
|
4天前
|
机器学习/深度学习 算法 数据挖掘
【C 言专栏】C 语言与机器学习的应用
【5月更文挑战第6天】C语言在机器学习中扮演关键角色,以其高效性、灵活性和可移植性实现底层算法、嵌入式系统和高性能计算。在神经网络、决策树和聚类算法等领域的实现中不可或缺。C语言被用于TensorFlow和OpenCV等知名库的底层,常与C++、Python结合使用。尽管面临开发难度和适应新算法的挑战,但C语言在机器学习领域的价值和潜力将持续展现,为科技进步贡献力量。
【C 言专栏】C 语言与机器学习的应用
|
6天前
|
存储 缓存 算法
【C 言专栏】C 语言中的数据结构应用
【5月更文挑战第4天】本文探讨了C语言中的核心数据结构,包括数组、链表(单链表和双链表)、栈、队列、二叉树(如二叉搜索树和二叉堆)以及图结构。这些数据结构在程序设计中扮演着关键角色,如数组的快速访问、链表的动态管理、栈和队列的处理流程控制、树和图的复杂关系表示。理解并选择适当的数据结构可优化程序性能,而内存管理和算法优化则进一步提升效率。通过案例分析和展望未来发展趋势,本文旨在帮助读者深化对C语言数据结构的理解和应用。
【C 言专栏】C 语言中的数据结构应用
|
9天前
|
机器学习/深度学习 算法 C语言
【C言专栏】递归算法在 C 语言中的应用
【4月更文挑战第30天】本文介绍了递归算法在C语言中的应用,包括基本概念(通过调用自身解决子问题)、特点(调用自身、终止条件、栈空间)和实现步骤(定义递归函数、分解问题、设置终止条件、组合解)。文中通过阶乘计算和斐波那契数列两个案例展示了递归的使用,强调了递归可能导致的栈溢出问题及优化需求。学习递归有助于理解和应用“分而治之”策略。
|
10天前
|
存储 算法 程序员
【C言专栏】C 语言结构体的应用与实践
【4月更文挑战第30天】C语言中的结构体是自定义数据类型的关键,它组合不同类型的數據以创建新类型,尤其适合处理复杂对象如学生信息。通过定义结构体如`struct Student`,包含名字、学号和成绩,可以方便地实例化和访问成员。结构体在链表实现、函数参数传递和数组中都有广泛应用,如表示链表节点和处理批量数据。理解并熟练运用结构体对于C语言编程至关重要,能提升代码效率和可读性。
|
10天前
|
存储 程序员 C语言
【C 言专栏】C 语言指针的深度解析
【4月更文挑战第30天】C 语言中的指针是程序设计的关键,它如同一把钥匙,提供直接内存操作的途径。指针是存储其他变量地址的变量,通过声明如`int *ptr`来使用。它们在动态内存分配、函数参数传递及数组操作中发挥重要作用。然而,误用指针可能导致错误,如空指针引用和内存泄漏。理解指针的运算、与数组和函数的关系,以及在结构体中的应用,是成为熟练 C 语言程序员的必经之路。虽然挑战重重,但掌握指针将增强编程效率和灵活性。不断实践和学习,我们将驾驭指针,探索更广阔的编程世界。
|
10天前
|
供应链 Java API
Java 8新特性解析及应用区块链技术在供应链管理中的应用与挑战
【4月更文挑战第30天】本文将深入探讨Java 8的新特性,包括Lambda表达式、Stream API和Optional类等。通过对这些新特性的详细解析和应用实例,帮助读者更好地理解和掌握Java 8的新技术。
|
10天前
|
存储 芯片
【期末不挂科-单片机考前速过系列P11】(第十一章:15题速过串行口的工作原理和应用)经典例题盘点(带图解析)
【期末不挂科-单片机考前速过系列P11】(第十一章:15题速过串行口的工作原理和应用)经典例题盘点(带图解析)

推荐镜像

更多