什么是指定初始化

在标准 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 编译器的扩展特性来提供给程序员使用。

指定初始化数组元素

在 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 语句中。比如下面的程序:

#include<stdio.h>
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;
}

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

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

跟数组类似,在标准 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,我们就可以给结构体变量的某一个指定成员直接赋值。非常方便。

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_MMUunsigned (*mmap_capabilities)(struct file *);#endif};

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

指定初始化的好处

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

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

Linux 内核C -- 第02课:驱动中的指定初始化相关推荐

  1. NanoPi NEO Air使用九:使用Linux内核自带的LED驱动

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

  2. Linux内核学习-字符设备驱动学习(二)

    在Linux内核学习-字符设备驱动学习(一)中编写字符设备驱动的一种方法,但是需要手动创建设备节点. 有没有能够自动的创建设备节点的呢? 有!使用class_create()和device_creat ...

  3. linux越狱amd卡代码,为Linux内核贡献27.5万行代码中:AMD意外泄漏下一代APU信息

    原标题:为Linux内核贡献27.5万行代码中:AMD意外泄漏下一代APU信息 AMD在不经意间泄露了自家下一代APU的信息,其代号"Van Gogh(梵高)". 据外媒报道称,A ...

  4. Linux内核USB总线--设备控制器驱动框架分析

    正文 1.概述 如下图所示,USB控制器可以呈现出两种不同的状态.USB控制器作为Host时,称为USB主机控制器,使用USB主机控制器驱动.USB控制器作为Device时,称为USB设备控制器,使用 ...

  5. Linux内核I/O报错信息中hostbyte与driverbyte含义

    文章目录 1.现象举例 2.hostbyte和driverbyte 3.FC链路的硬件故障 4.源码分析 1.现象举例 1.hostbyte=DID_BAD_TARGET driverbyte=DRI ...

  6. Linux内核网络栈1.2.13-网卡设备的初始化流程

    参考资料 <<linux内核网络栈源代码情景分析>> 网卡设备的初始化 本文主要描述一下网卡设备的整个初始化的过程,该过程主要就是根据设备的硬件信息来获取与传输网络数据,注册相 ...

  7. 【linux】使用sed删除大文件中的指定行

    [linux]使用sed删除大文件中的指定行 引言: 最近在清洗数据时遇到一个超大的文件,需要删除其中不符合数据要求的指定行.使用之前写好的python脚本清洗速度太慢,于是检索了许多linux文件处 ...

  8. Ubuntu 18.04 Linux内核升级(因为在系统中安装会出现各种驱动不兼容的问题,所以去官网下)

    前言 原本使用Ubuntu 18.04.2 LTS来换到5.4.45的内核版本,来尝试在系统中直接用sudo apt-get install linux-image--命令更新一下Linux的内核,但 ...

  9. linux内核部件分析之——设备驱动模型之class

    前面看过了设备驱动模型中的bus.device.driver,这三种都是有迹可循的.其中bus代表实际的总线,device代表实际的设备和接口,而driver则对应存在的驱动.但本节要介绍的class ...

  10. 在linux内核3.14.43添加自己的驱动源码,linux内核如何加入自己的驱动

    linux添加自己的驱动,内核源码版本:3.14.43 1. 添加自己的文件夹 在驱动里面加入自己的文件夹,例如我在driver/char/文件夹里面加入自己的pwm驱动,我需要在char目录下,新建 ...

最新文章

  1. php xss速查表,2020跨站脚本[xss]速查表 xss cheat sheet
  2. nonzero的用法一则例子
  3. java深拷贝和浅拷贝介绍
  4. fullcalendar 显示的时间间隔只有四十五分钟_NHR系列智能显示控制仪表RS485通信中应用...
  5. 据说,80%的人没有真正理解了Spring的依赖注入
  6. C++二分查找,时间复杂度是O(logn)
  7. Screaming Frog SEO Spide如何解决IIS 10中缺少CS协议支持的问题
  8. Ubuntu安装语言包
  9. Javascript格式化工具
  10. 符合功能安全要求的动态测试工具-TESSY
  11. ESXi 6 升级到ESXi 6.5VIB冲突问题
  12. discourse 安装_如何使用Discourse在半天之内建立内部团队论坛
  13. Android_openCV图片处理
  14. CollaNote - 完全免费无广告的 iPad / iPhone 手写笔记应用(Notability / GoodNotes 的免费替代品)
  15. 最新短网址生成api接口(t.cn、url.cn短链接生成)
  16. 【爱码物联】“颜值经济”当道,你选的化妆品能溯源么?
  17. mysql:比较大小
  18. JAVA租车网站计算机毕业设计Mybatis+系统+数据库+调试部署
  19. Unity Shader 实现卡通渲染效果
  20. 域名含义解释查询_域名状态的含义说明

热门文章

  1. [iOS]delegate和protocol
  2. cocos2d-x关于CCTableView的“乱序问题”的理解
  3. CCV 调试 (一)
  4. .NET之对接口和抽象类(二)
  5. Netty4.0学习笔记系列之一:Server与Client的通讯
  6. cocos2d-x自制工具07:打印cocos2d-x的节点树
  7. tkMessageBox only show one window
  8. 视频云存储平台 备忘
  9. TCP/IP FTP/TFTP
  10. 数字 三位一节(逗号隔开)表示