00. 目录

文章目录

  • 00. 目录
  • 01. 初始化概述
  • 02. 指定初始化数组元素
  • 03. 指定初始化结构体成员变量
  • 04. Linux内核中应用
  • 05. 初始化总结
  • 06. 附录

01. 初始化概述

在标准 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 a[100] ={ [10] = 1, [30] = 2};

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

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

02. 指定初始化数组元素

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

程序示例

#include <stdio.h>int main(void)
{int i = 0;int a[10] = {[3] = 3, [5] = 5, [8] = 8};for (i = 0; i < 10; i++){printf("a[%d] = %d\n", i, a[i]);}return 0;
}

执行结果

deng@itcast:~/tmp$ gcc 2array.c
deng@itcast:~/tmp$ ./a.out
a[0] = 0
a[1] = 0
a[2] = 0
a[3] = 3
a[4] = 0
a[5] = 5
a[6] = 0
a[7] = 0
a[8] = 8
a[9] = 0

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

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

程序示例

#include <stdio.h>int main(void)
{int i = 0;//注意 ...之间不能有空格int a[10] = {[0 ... 4] = 1, [5 ... 9] = 2};for (i = 0; i < 10; i++){printf("a[%d] = %d\n", i, a[i]);}return 0;
}

执行结果

deng@itcast:~/tmp$ gcc 2array.c
deng@itcast:~/tmp$ ./a.out
a[0] = 1
a[1] = 1
a[2] = 1
a[3] = 1
a[4] = 1
a[5] = 2
a[6] = 2
a[7] = 2
a[8] = 2
a[9] = 2

在这个程序中,我们使用 [0 … 4] 表示一个索引范围,相当于给 a[0] 到 a[4] 之间的5个数组元素赋值为1。

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

程序示例

#include <stdio.h>int main(void)
{int i = 3;switch(i){case 1:printf("1\n");break;case 2 ... 8:printf("2...8\n");break;case 9:printf("9\n");default:printf("default\n");break;}return 0;
}

执行结果

deng@itcast:~/tmp$ gcc 3case.c
deng@itcast:~/tmp$ ./a.out
2...8

在这个程序中,当 case 值为2到8时,都执行相同的 case 分支,可以通过 case 2 … 8: 的形式来简化代码。这

温馨提示

… 和其两端的数据范围2和8之间也要空格,不能写成2…8的形式,否则编译就会通不过。

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

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

程序示例

#include <stdio.h>typedef struct _stu_t
{int id;int age;char sex;
}stu_t;int main(void)
{stu_t s1 = {1, 18, 'M'};stu_t s2 = {.id = 2,.age = 24,.sex = 'F'};printf("s1 id:%d age: %d sex: %c\n", s1.id, s1.age, s1.sex);printf("s2 id:%d age: %d sex: %c\n", s2.id, s2.age, s2.sex);return 0;
}

执行结果

deng@itcast:~/tmp$ gcc 4struct.c
deng@itcast:~/tmp$ ./a.out
s1 id:1 age: 18 sex: M
s2 id:2 age: 24 sex: F

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

04. 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 (*iopoll)(struct kiocb *kiocb, bool spin);int (*iterate) (struct file *, struct dir_context *);int (*iterate_shared) (struct file *, struct dir_context *);__poll_t (*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 *);unsigned long mmap_supported_flags;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 (*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 (*setfl)(struct file *, unsigned long);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 *);
#endifssize_t (*copy_file_range)(struct file *, loff_t, struct file *,loff_t, size_t, unsigned int);loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,struct file *file_out, loff_t pos_out,loff_t len, unsigned int remap_flags);int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;

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

05. 初始化总结

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

我们通过指定初始化方式,就可以避免这个问题。无论file_operations 结构体类型如何变化,添加成员也好、减少成员也好、调整成员顺序也好,都不会影响其它文件的使用。

06. 附录

【嵌入式】C语言高级编程-数组和结构体初始化(02)相关推荐

  1. C++ 数组与结构体初始化列表

    1.数组与结构体初始化列表 #include <iostream> using namespace std; struct stu {     int a;     float b; }; ...

  2. C语言常用编程技巧-结构体数组

    本文主要用来记录C语言常用编程技巧,完成一下目的: 1.函数指针如何定义使用: 2.结构体数组如何定义使用: 3.callback 方法使用技巧: 以下代码为在ubuntu 系统进行编译,文件名为 s ...

  3. c语言中二维数组的结构体,怎么才能把结构体里面的二维数组打印出来?

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 萌新一位,学到结构体在做作业,遇到了这个问题.求提示 出现这样的报错:[Error] cannot convert 'char (*)[20]' to ' ...

  4. 数组,结构体初始化 {0}

    一直以为 int a[256]={0};是把a的所有元素初始化为0,int a[256]={1};是把a所有的元素初始化为1. 调试的时查看内存发现不是那么一回事,翻了一下<The C++ Pr ...

  5. 数组,结构体初始化 {0} (转载)

    一直以为 int a[256]={0};是把a的所有元素初始化为0,int a[256]={1};是把a所有的元素初始化为1. 调试的时查看内存发现不是那么一回事,翻了一下<The C++ Pr ...

  6. 【嵌入式】C语言高级编程-嵌入式C语言简介(01)

    00. 目录 文章目录 00. 目录 01. C 语言标准 02. C 标准内容 03. C 标准的发展历程 04. 编译器对 C 标准的支持 05. 编译器对 C 标准的扩展 06. Linux内核 ...

  7. c语言高级程序设计第五版PDF,C语言高级编程.pdf

    C语言高级编程 概述 由几个测试程序说开去 预编译与宏 高级预编译介绍 宏的高级用法 变量 变量分类详细解析 我的变量去哪儿了? 大小端对变量的影响 内存与指针 常见内存使用错误大观 指针,又是指针! ...

  8. 北京linux嵌入式培训,北京嵌入式培训上嵌Linux开发基础和嵌入式C语言初级编程总结...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 说到学习Linux开发基础,有一样是不得不说,而且Llinux系统中最常用也最有用的东西,那就是各种命令.虽然Linux桌面应用发展很快,但是命 令在Li ...

  9. Go 学习推荐 —(Go by example 中文版、Go 构建 Web 应用、Go 学习笔记、Golang常见错误、Go 语言四十二章经、Go 语言高级编程)

    Go by example 中文版 Go 构建 Web 应用 Go 学习笔记:无痕 Go 标准库中文文档 Golang开发新手常犯的50个错误 50 Shades of Go: Traps, Gotc ...

最新文章

  1. CentOS中vsftp安装与配置
  2. 从头开始 Struts 2 入门
  3. 第104天:web字体图标使用方法
  4. php 获取鼠标的坐标,如何实时获取鼠标的当前坐标-
  5. 计算机网络基础学测,《计算机网络技术基础》第二1章单元学习测习题-20210622072616.docx-原创力文档...
  6. android开启服务器配置,Android基于XMPP开发(一)【openfire服务器配置】
  7. python tkinter text改变文本字体颜色_如何更改Tkinter中文本的颜色?
  8. java build返回空值_OKHTTP拦截器不断返回空值并使应用程序崩溃
  9. java secondtotime_Java中的LocalTime toSecondOfDay()方法
  10. python 给文本加下划线_untiy3dUGUI实现text文本下划线
  11. Android源码编译FFmpeg4.0.2单独so库for Android(验证可用)
  12. 73 ----空间曲线的投影、投影柱面与投影曲线的方程、二元函数的等值线、等高线的性质
  13. 倒立摆 adams matlab,基于adams和matlab的一级倒立摆联合仿真
  14. wps如何将字体竖着排列_WPS文字中怎么竖着打字?wps文字竖排的设置方法介绍
  15. 合成器基础(三) - 减法合成器的工作原理
  16. JavaScript在线代码编辑器-技术选型
  17. 慧之声科技- 程序员的爱情故事
  18. 数字电路-逻辑式化简公式
  19. 赛码python多行输入框_在线编程中的数据读取问题牛科网,牛客,赛码
  20. themeleaf基本语法

热门文章

  1. Windows 系统下Git安装图解
  2. 安装Apache的步骤
  3. MS讲座:可视化的软件架构设计和Portal Starter Kit挖宝记
  4. linux创建表空间 没有权限,Linux oracle数据库创建表空间、用户并赋予权限
  5. 剪裁tiff影像数据_能看更会用,超擎影像云平台带你轻松玩转海量影像!
  6. mysql启用组提交变量_MySQL的COMMIT_ORDER模式下组提交分组实现与BUG案例源码剖析...
  7. 【2012百度之星/资格赛】H:用户请求中的品牌
  8. 用js实现表格行的动态添加与删除
  9. (转)解决fasterxml中string字符串转对象json格式错误问题(无引号 单引号问题)...
  10. 20155238 实验四 Android程序设计