千家信息网

嵌入式C语言自我修养 02:Linux 内核驱动中的指定初始

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,2.1 什么是指定初始化在标准 C 中,当我们定义并初始化一个数组时,常用方法如下:int a[10] = {0,1,2,3,4,5,6,7,8};按照这种固定的顺序,我们可以依次给 a[0] 和 a
千家信息网最后更新 2026年11月02日嵌入式C语言自我修养 02:Linux 内核驱动中的指定初始

2.1 什么是指定初始化

在标准 C 中,当我们定义并初始化一个数组时,常用方法如下:

int a[10] = {0,1,2,3,4,5,6,7,8};

按照这种固定的顺序,我们可以依次给 a[0] 和 a[8] 赋值。因为没有对 a[9] 赋值,所以编译器会将 a[9] 默认设置为0。当数组长度比较小时,使用这种方式初始化比较方便。当数组比较大,而且数组里的非零元素并不连续时,这时候再按照固定顺序初始化就比较麻烦了。

比如,我们定义一个数组 b[100],其中 b[10]、b[30] 需要初始化,如果还按照前面的固定顺序初始化,{}中的初始化数据中间可能要填充大量的0,比较麻烦。

那怎么办呢?C99 标准改进了数组的初始化方式,支持指定任意元素初始化,不再按照固定的顺序初始化。

int b[100] ={ [10] = 1, [30] = 2};

通过数组索引,我们可以直接给指定的数组元素赋值。除此之外,一个结构体变量的初始化,也可以通过指定某个结构体域直接赋值。

因为 GNU C 支持 C99 标准,所以 GCC 编译器也支持这一特性。甚至早期不支持 C99,只支持 C89 的 GCC 编译器版本,这一特性也被当作一个 GCC 编译器的扩展特性来提供给程序员使用。

2.2 指定初始化数组元素

在 GNU C 中,通过数组元素索引,我们就可以给某个指定的元素直接赋值。

int b[100] = { [10] = 1, [30] = 2 };

在{ }中,我们通过 [10] 数组元素索引,就可以直接给 a[10] 赋值了。这里有个细节注意一下,就是各个赋值之间用逗号 "," 隔开,而不是使用分号";"。

如果我们想给数组中某一个索引范围的数组元素初始化,可以采用下面的方式。

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] 表示一个索引范围,相当于给 a[10] 到 a[30] 之间的20个数组元素赋值为1。

GNU C 支持使用 ... 表示范围扩展,这个特性不仅可以使用在数组初始化中,也可以使用在 switch-case 语句中。比如下面的程序:

#includeint 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;}

在这个程序中,当 case 值为2到8时,都执行相同的 case 分支,可以通过 case 2 ... 8: 的形式来简化代码。这里同样也有一个细节需要注意,就是 ... 和其两端的数据范围2和8之间也要空格,不能写成2...8的形式,否则编译就会通不过。

2.3 指定初始化结构体成员变量

跟数组类似,在标准 C 中,结构体变量的初始化也要按照固定的顺序。在 GNU C 中我们也可以通过结构域来初始化指定某个成员。

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

在程序中,我们定义一个结构体类型 student,然后分别定义两个结构体变量 stu1 和 stu2。初始化 stu1 时,我们采用标准 C 的初始化方式,即按照固定顺序直接初始化。初始化 stu2 时,我们采用 GNU C 的初始化方式,通过结构域名 .name 和 .age,我们就可以给结构体变量的某一个指定成员直接赋值。非常方便。

2.4 Linux 内核驱动注册

在 Linux 内核驱动中,大量使用 GNU C 的这种指定初始化方式,通过结构体成员来初始化结构体变量。比如在字符驱动程序中,我们经常见到这样的初始化:

static const struct file_operations ab3100_otp_operations = {.open        = ab3100_otp_open,.read        = seq_read,.llseek        = seq_lseek,.release    = single_release,};

在驱动程序中,我们经常使用 file_operations 这个结构体变量来注册我们开发的驱动,然后以回调的方式来执行我们驱动实现的相关功能。结构体 file_operations 在 Linux 内核中的定义如下:

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

结构体 file_operations 里面定义了很多结构体成员,而在这个驱动中,我们只初始化了部分成员变量,通过访问结构体的成员来指定初始化,非常方便。

2.5 指定初始化的好处

这种指定初始化方式,不仅使用灵活,而且还有一个好处就是:代码易于维护。尤其是在 Linux 内核这种大型项目中,几万个文件,几千万的代码量,当成百上千个文件都使用 file_operations 这个结构体类型来定义变量并初始化时,那么一个很大的问题就来了:如果采用标准 C 那种按照固定顺序赋值,当我们的 file_operations 结构体类型发生改变时,如添加成员、减少成员、调整成员顺序,那么使用该结构体类型定义变量的大量 C 文件都需要重新调整初始化顺序,牵一发而动全身,想想这是多么可怕!

我们通过指定初始化方式,就可以避免这个问题。无论file_operations 结构体类型如何变化,添加成员也好、减少成员也好、调整成员顺序也好,都不会影响其它文件的使用。有了指定初始化,再也不用加班修改代码了,妈妈再也不用担心我们整日加班,不回家吃饭了,多好!

本教程根据 C语言嵌入式Linux高级编程视频教程 第05期 改编,电子版书籍可加入QQ群:475504428 下载,更多嵌入式视频教程,可关注:
微信公众号:宅学部落(armlinuxfun)
51CTO学院-王利涛老师:http://edu.51cto.com/sd/d344f

结构 数组 成员 变量 顺序 元素 方式 驱动 程序 标准 支持 类型 索引 编译 内核 代码 文件 特性 编译器 范围 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 美国互联网保险保险科技 金融科技与互联网金融 易互联网科技有限公司 致聘互联网科技 太原新华互联网科技学校怎么样 互联网企业和科技型企业 电子科技大学互联网创业大赛 迪庆互联网科技招生 华夏恒生互联网科技013171 佛山市拉法互联网科技有限公司 世界互联网领先科技成果获奖 互联网科技如何创业 沐思想互联网科技有限公司 互联网企业科技类企业如何做账 济南小医互联网科技有限公司招聘 银川新华互联网科技学校是国营吗 云南互联网科技学费 玉溪新华互联网科技怎么选 安徽零酒互联网科技有限公司 互联网科技自媒体灵感 互联网科技工作委员会主任 中国互联网科技股 小马过河互联网科技 北京禾田雨橡互联网科技 互联网之后的科技是什么 来此购互联网科技有限公司 辽宁互联网新科技 华夏恒生互联网科技013171 广州富喜互联网络科技 第三届互联网大会安防黑科技
0