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/

最后,如果您覺得我的文章對您所有幫助,歡迎關注我的個人公眾號
在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM