在阅读GNU/Linux内核代码时,我们会遇到一种特殊的结构初始化方式。该方式是某些C教材(如谭二版、K&R二版)中没有介绍过的。这种方式称为指定初始化(designated initializer)。下面我们看一个例子,Linux-2.6.x/drivers/usb/storage/usb.c中有这样一个结构体初始化项目:

static struct usb_driver
usb_storage_driver = {

.owner = THIS_MODULE,

.name = "usb-storage",

.probe = storage_probe,

.disconnect =
storage_disconnect,

.id_table =
storage_usb_ids,

};

乍一看,这与我们之前学过的结构体初始化差距甚远。其实这就是前面所说的指定初始化在Linux设备驱动程序中的一个应用,它源自ISO
C99标准。以下我摘录了C Primer
Plus第五版中相关章节的内容,从而就可以很好的理解2.6版内核采用这种方式的优势就在于由此初始化不必严格按照定义时的顺序。这带来了极大的灵活性,其更大的益处还有待大家在开发中结合自身的应用慢慢体会。

已知一个结构,定义如下

struct book {

char title[MAXTITL];

char author[MAXAUTL];

float value;

};

C99支持结构的指定初始化项目,其语法与数组的指定初始化项目近似。只是,结构的指定初始化项目使用点运算符和成员名(而不是方括号和索引值)来标识具体的元素。例如,只初始化book结构的成员value,可以这样做:

struct book surprise = { .value
= 10.99 };

可以按照任意的顺序使用指定初始化项目:

struct book gift = {
.value = 25.99,

.author = "James
Broadfool",

.title = "Rue for the
Toad"};

正像数组一样,跟在一个指定初始化项目之后的常规初始化项目为跟在指定成员后的成员提供了初始值。另外,对特定成员的最后一次赋值是它实际获得的值。例如,考虑下列声明:

struct book gift = { .value =
18.90,

.author = "Philionna
pestle",

0.25};

这将把值0.25赋给成员value,因为它在结构声明中紧跟在author成员之后。新的值0.25代替了早先的赋值18.90。

有关designated initializer的进一步信息可以参考c99标准的6.7.8节Ininialization。

这篇转载大致解决了我们疑惑的问题。其实很简单,绕来绕去的,不就是初始化吗!
  
但是随后又在c语言扩展中找到了如下的起码是我不知道的事情。

标准C89需要初始化语句的元素以固定的顺序出现,和被初始化的数组或结构体中的元素顺序一样。在ISO
C99中,你可以按任何顺序给出这些元素,指明它们对应的数组的下标或结构体的成员名,并且GNU
C也把这作为C89模式下的一个扩展。这个扩展没有在GNU C++中实现。



   为了指定一个数组下标,在元素值的前面写上“[index]
=”。比如:

int a[6] = { [4] = 29, [2] = 15 };



相当于:

int a[6] = { 0, 0, 15, 0, 29, 0 };



下标值必须是常量表达式,即使被初始化的数组是自动的。



一个可替代这的语法是在元素值前面写上“.[index]”,没有“=”,但从GCC
2.5开始就不再被使用,但GCC仍然接受。为了把一系列的元素初始化为相同的值,写为“[first ... last] =
value”。这是一个GNU扩展。比如:

int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };



如果其中的值有副作用,这个副作用将只发生一次,而不是范围内的每次初始化一次。



注意,数组的长度是指定的最大值加一。



在结构体的初始化语句中,在元素值的前面用“.fieldname = ”指定要初始化的成员名。例如,给定下面的结构体,

struct point { int x, y; };



和下面的初始化,

struct point p = { .y = yvalue, .x = xvalue };



等价于:

struct point p = { xvalue, yvalue };



另一有相同含义的语法是“.fieldname:”,不过从GCC 2.5开始废除了,就像这里所示:

struct point p = { y: yvalue, x: xvalue };



“[index]”或“.fieldname”就是指示符。在初始化共同体时,你也可以使用一个指示符(或不再使用的冒号语法),来指定共同体的哪个元素应该使用。比如:

union foo { int i; double d; };

        union
foo f = { .d = 4 };



将会使用第二个元素把4转换成一个double类型来在共同体存放。相反,把4转换成union
foo类型将会把它作为整数i存入共同体,既然它是一个整数。(参考5.24节向共同体类型转换。)



你可以把这种命名元素的技术和连续元素的普通C初始化结合起来。每个没有指示符的初始化元素应用于数组或结构体中的下一个连续的元素。比如,

int a[6] = { [1] = v1, v2, [4] = v4 };



等价于

int a[6] = { 0, v1, v2, 0, v4, 0 };



当下标是字符或者属于enum类型时,标识数组初始化语句的元素特别有用。例如:

int whitespace[256]

= { [' '] = 1, ['\t'] = 1, ['\h'] = 1,

['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };



你也可以在“=”前面写上一系列的“.fieldname”和“[index]”指示符来指定一个要初始化的嵌套的子对象;这个列表是相对于和最近的花括号对一致的子对象。比如,用上面的struct
point声明:

struct point ptarray[10] = {
[2].y = yv2, [2].x = xv2, [0].x = xv0 };



如果同一个成员被初始化多次,它将从最后一次初始化中取值。如果任何这样的覆盖初始化有副作用,副作用发生与否是非指定的。目前,gcc会舍弃它们并产生一个警告。

Linux C中结构体初始化的更多相关文章

  1. Linux下C结构体初始化[总结]

    1.前言 今天在公司看一同事写的代码,代码中用到了struct,初始化一个struct用的是乱序格式,如下代码所示: typedef struct _data_t { int a; int b; }d ...

  2. Linux下C结构体初始化

    1.前言 今天在公司看一同事写的代码,代码中用到了struct,初始化一个struct用的是乱序格式,如下代码所示: typedef struct _data_t { int a; int b; }d ...

  3. Linux2.6 内核中结构体初始化(转载)

    转自:http://hnniyan123.blog.chinaunix.net/uid-29917301-id-4989879.html 在Linux2.6版本的内核中,我们经常可以看到下面的结构体的 ...

  4. C语言结构体初始化方法

    早上苏凯童鞋问我这个问题来着,写在这里. 我了解到的C中结构体初始化的方法大概有三种. 如这里我定义了一个结构体: typedef struct node { int x, y; }Node; 第一种 ...

  5. Linux C 结构体初始化三种形式

    最近看linux代码时发现了结构体 struct 一种新的初始化方式,各方查找对比后总结如下: 1. 顺序初始化教科书上讲C语言结构体初始化是按照顺序方式来讲的,没有涉及到乱序的方式.顺序初始化str ...

  6. 关于c语言中结构体的初始化

    1.先定义结构体类型后再定义结构体变量: 格式为:struct 结构体名 变量名列表: struct book s1,s2,*ss://注意这种之前要先定义结构体类型后再定义变量: 2.在定义结构体类 ...

  7. Linux C语言结构体-学习笔记

    Linux C语言结构体简介 前面学习了c语言的基本语法特性,本节进行更深入的学习. 预处理程序. 编译指令: 预处理, 宏定义, 建立自己的数据类型:结构体,联合体,动态数据结构 c语言表达式工具 ...

  8. Linux内核kobject结构体分析

    1.前言 Linux内核中有大量的驱动,而这些驱动往往具有类似的结构,根据面向对象的思想,可以将共同的部分提取为父类,而这个父类就是kobject,kobject结构体中包含了大量设备的必须信息,而三 ...

  9. (一)一个工作任务引起的乱战——c#中结构体与byte[]间相互转换

    一个工作任务涉及到c#与c++系统间的udp通信,处理了蛮长时间没有完成任务,但是期间接触到不少小知识点.本人是初接触c#,c++语言没有接触过.可能写的东西都很小儿科,暂且记录下来当工作日记把. 先 ...

随机推荐

  1. Linux换源+编译内核总结

    换源: 我用的是CentOS,所以下面以其为例,其它OS做法类似,可作参考: 在主机能联网的情况下 进入存放源配置的文件夹 cd /etc/yum.repos.d 备份默认源 mv ./CentOS- ...

  2. python基础(六)循环

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 循环用于重复执行一些程序块.从上一讲的选择结构,我们已经看到了如何用缩进来表示程序 ...

  3. PDO链接mysql学习笔记

    <?php //PDO链接mysql//dsn三种写法: //dsn01 $dsn = 'mysql:host=localhost;dbname=mysql'; //$dsn = 'mysql: ...

  4. centos---无线上网的电脑所安装的虚拟机网络设置

    1.共享设置 找到本地的无线网卡,右键---属性---共享,设置如下 2.修改VMware Network Adapter VMnet1 选中VMware Network Adapter VMnet1 ...

  5. URL编码知识摘抄备忘

    网页工具 http://www.107000.com/T-UrlEncode/ 参考: 维基百科http://zh.wikipedia.org/zh/%E7%99%BE%E5%88%86%E5%8F% ...

  6. 【转载】Android Metro风格的Launcher开发系列第二篇

    前言: 各位小伙伴们请原谅我隔了这么久才开始写这一系列的第二篇博客,没办法忙新产品发布,好了废话不说了,先回顾一下:在我的上一篇博客Android Metro风格的Launcher开发系列第一篇写了如 ...

  7. Android常见的按钮监听器实现方式

    为按钮(Button)添加响应事件,需要为其设置监听器(Listener).本文总结了Android中常用的几种Button Listener. 第一种:匿名内部类作为事件监听器类 1 2 3 4 5 ...

  8. ZBrush中怎样对遮罩进行反选

    通过对ZBrush的学习,我们知道了如何手动创建遮罩,手动创建遮罩相对来说是最简单有效的方法,在某些特定的使用场合会起到事半功倍的效果.创建遮罩我们可以结合Ctrl键在物体保持编辑的状态下来执行,您可 ...

  9. go 字符变量

    go语言变量定义 第一类,通过关键字var来声明,可以在main函数体外 // varStudy //变量在main函数体外声明 package main import ( "fmt&quo ...

  10. Luogu题目集合[6/未完待续]

    1. P1327数列排序 题目描述 给定一个数列{an},这个数列满足ai≠aj(i≠j),现在要求你把这个数列从小到大排序,每次允许你交换其中任意一对数,请问最少需要几次交换? 输入输出格式 输入格 ...