千家信息网

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

发表于:2025-02-06 作者:千家信息网编辑
千家信息网最后更新 2025年02月06日,2.1 什么是指定初始化在标准 C 中,当我们定义并初始化一个数组时,常用方法如下:int a[10] = {0,1,2,3,4,5,6,7,8};按照这种固定的顺序,我们可以依次给 a[0] 和 a
千家信息网最后更新 2025年02月06日嵌入式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安全错误 数据库的锁怎样保障安全 dns服务器地址自己变了 修改数据库文件名时 南海网络安全知识 滨湖区软件开发来电咨询 朝阳区技术软件开发大概费用 日本语软件开发 中国计算机服务器芯片何时能突破 软件开发设计标准化文件 车载网络技术计算题 浙江2u存储服务器机箱是什么 sql数据库导出到网页 软件开发专业和计算机 网络安全领域漏洞怎么办 软件开发要做哪些映射 服务器不建网站还用备案吗 崇明涉密信息系统集成软件开发 linq保存数据库 各学校观看网络安全宣传周直播 天津服务器迁移公司有哪些云空间 vba连接两个数据库 frps一键服务器配置 收银系统用什么软件开发 国企软件开发干不好会被辞退嘛 学习数据库系统原理有什么用 媒资服务器是什么意思 什么软件传输苹果手机数据库 软件开发 投标技术 软件开发添加定位多少钱 因特网上一个服务器 抚顺盎凑网络技术有限公司
0