在標(biāo)準(zhǔn) C 中,當(dāng)我們定義并初始化一個數(shù)組時,常用方法如下:
創(chuàng)新互聯(lián)2013年至今,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目網(wǎng)站建設(shè)、成都網(wǎng)站制作網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元龍灣做網(wǎng)站,已為上家服務(wù),為龍灣各地企業(yè)和個人服務(wù),聯(lián)系電話:18982081108
int a[10] = {0,1,2,3,4,5,6,7,8};
按照這種固定的順序,我們可以依次給 a[0] 和 a[8] 賦值。因為沒有對 a[9] 賦值,所以編譯器會將 a[9] 默認(rèn)設(shè)置為0。當(dāng)數(shù)組長度比較小時,使用這種方式初始化比較方便。當(dāng)數(shù)組比較大,而且數(shù)組里的非零元素并不連續(xù)時,這時候再按照固定順序初始化就比較麻煩了。
比如,我們定義一個數(shù)組 b[100],其中 b[10]、b[30] 需要初始化,如果還按照前面的固定順序初始化,{}中的初始化數(shù)據(jù)中間可能要填充大量的0,比較麻煩。
那怎么辦呢?C99 標(biāo)準(zhǔn)改進(jìn)了數(shù)組的初始化方式,支持指定任意元素初始化,不再按照固定的順序初始化。
int b[100] ={ [10] = 1, [30] = 2};
通過數(shù)組索引,我們可以直接給指定的數(shù)組元素賦值。除此之外,一個結(jié)構(gòu)體變量的初始化,也可以通過指定某個結(jié)構(gòu)體域直接賦值。
因為 GNU C 支持 C99 標(biāo)準(zhǔn),所以 GCC 編譯器也支持這一特性。甚至早期不支持 C99,只支持 C89 的 GCC 編譯器版本,這一特性也被當(dāng)作一個 GCC 編譯器的擴展特性來提供給程序員使用。
在 GNU C 中,通過數(shù)組元素索引,我們就可以給某個指定的元素直接賦值。
int b[100] = { [10] = 1, [30] = 2 };
在{ }中,我們通過 [10] 數(shù)組元素索引,就可以直接給 a[10] 賦值了。這里有個細(xì)節(jié)注意一下,就是各個賦值之間用逗號 “,” 隔開,而不是使用分號“;”。
如果我們想給數(shù)組中某一個索引范圍的數(shù)組元素初始化,可以采用下面的方式。
int main(void)
{
int b[100] = { [10 ... 30] = 1, [50 ... 60] = 2 };
for(int i = 0; i < 100; i++)
{
printf("%d ", a[i]);
if( i % 10 == 0)
printf("\n");
}
return 0;
}
在這個程序中,我們使用 [10 ... 30] 表示一個索引范圍,相當(dāng)于給 a[10] 到 a[30] 之間的20個數(shù)組元素賦值為1。
GNU C 支持使用 ... 表示范圍擴展,這個特性不僅可以使用在數(shù)組初始化中,也可以使用在 switch-case 語句中。比如下面的程序:
#include
int main(void)
{
int i = 4;
switch(i)
{
case 1:
printf("1\n");
break;
case 2 ... 8:
printf("%d\n",i);
break;
case 9:
printf("9\n");
break;
default:
printf("default!\n");
break;
}
return 0;
}
在這個程序中,當(dāng) case 值為2到8時,都執(zhí)行相同的 case 分支,可以通過 case 2 ... 8: 的形式來簡化代碼。這里同樣也有一個細(xì)節(jié)需要注意,就是 ... 和其兩端的數(shù)據(jù)范圍2和8之間也要空格,不能寫成2...8的形式,否則編譯就會通不過。
跟數(shù)組類似,在標(biāo)準(zhǔn) C 中,結(jié)構(gòu)體變量的初始化也要按照固定的順序。在 GNU C 中我們也可以通過結(jié)構(gòu)域來初始化指定某個成員。
struct student{
char name[20];
int age;
};
int main(void)
{
struct student stu1={ "wit",20 };
printf("%s:%d\n",stu1.name,stu1.age);
struct student stu2=
{
.name = "wanglitao",
.age = 28
};
printf("%s:%d\n",stu2.name,stu2.age);
return 0;
}
在程序中,我們定義一個結(jié)構(gòu)體類型 student,然后分別定義兩個結(jié)構(gòu)體變量 stu1 和 stu2。初始化 stu1 時,我們采用標(biāo)準(zhǔn) C 的初始化方式,即按照固定順序直接初始化。初始化 stu2 時,我們采用 GNU C 的初始化方式,通過結(jié)構(gòu)域名 .name 和 .age,我們就可以給結(jié)構(gòu)體變量的某一個指定成員直接賦值。非常方便。
在 Linux 內(nèi)核驅(qū)動中,大量使用 GNU C 的這種指定初始化方式,通過結(jié)構(gòu)體成員來初始化結(jié)構(gòu)體變量。比如在字符驅(qū)動程序中,我們經(jīng)常見到這樣的初始化:
static const struct file_operations ab3100_otp_operations = {
.open = ab3100_otp_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
在驅(qū)動程序中,我們經(jīng)常使用 file_operations 這個結(jié)構(gòu)體變量來注冊我們開發(fā)的驅(qū)動,然后以回調(diào)的方式來執(zhí)行我們驅(qū)動實現(xiàn)的相關(guān)功能。結(jié)構(gòu)體 file_operations 在 Linux 內(nèi)核中的定義如下:
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 *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *,
unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *,
struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *,
struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
};
結(jié)構(gòu)體 file_operations 里面定義了很多結(jié)構(gòu)體成員,而在這個驅(qū)動中,我們只初始化了部分成員變量,通過訪問結(jié)構(gòu)體的成員來指定初始化,非常方便。
這種指定初始化方式,不僅使用靈活,而且還有一個好處就是:代碼易于維護(hù)。尤其是在 Linux 內(nèi)核這種大型項目中,幾萬個文件,幾千萬的代碼量,當(dāng)成百上千個文件都使用 file_operations 這個結(jié)構(gòu)體類型來定義變量并初始化時,那么一個很大的問題就來了:如果采用標(biāo)準(zhǔn) C 那種按照固定順序賦值,當(dāng)我們的 file_operations 結(jié)構(gòu)體類型發(fā)生改變時,如添加成員、減少成員、調(diào)整成員順序,那么使用該結(jié)構(gòu)體類型定義變量的大量 C 文件都需要重新調(diào)整初始化順序,牽一發(fā)而動全身,想想這是多么可怕!
我們通過指定初始化方式,就可以避免這個問題。無論file_operations 結(jié)構(gòu)體類型如何變化,添加成員也好、減少成員也好、調(diào)整成員順序也好,都不會影響其它文件的使用。有了指定初始化,再也不用加班修改代碼了,媽媽再也不用擔(dān)心我們整日加班,不回家吃飯了,多好!
本教程根據(jù) C語言嵌入式Linux高級編程視頻教程 第05期 改編,電子版書籍可加入QQ群:475504428 下載,更多嵌入式視頻教程,可關(guān)注:
微信公眾號:宅學(xué)部落(armlinuxfun)
51CTO學(xué)院-王利濤老師:http://edu.51cto.com/sd/d344f