本文主要是介绍标记化结构初始化语法---结构体成员前加小数点,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
对结构体
- struct a {
- int b;
- int c;
- }
有几种初始化方式:
- struct a a1 = {
- .b = 1,
- .c = 2
- };
或者
- struct a a1 = {
- b:1,
- c:2
- }
或者
- struct a a1 = {1, 2};
内核喜欢用第一种,使用第一种和第二种时,成员初始化顺序可变。
标记化结构初始化语法
在Linux2.6内核中对结构体的定义形式发生了变化,不再支持原来的定义形式。
- static struct tty_operations uart_ops =
- {
- .open = uart_open,//串口打开
- .close = uart_close,//串口关闭
- .write = uart_write,//串口发送
- .put_char = uart_put_char,//...
- .flush_chars = uart_flush_chars,
- .write_room = uart_write_room,
- .chars_in_buffer= uart_chars_in_buffer,
- .flush_buffer = uart_flush_buffer,
- .ioctl = uart_ioctl,
- .throttle = uart_throttle,
- .unthrottle = uart_unthrottle,
- .send_xchar = uart_send_xchar,
- .set_termios = uart_set_termios,
- .stop = uart_stop,
- .start = uart_start,
- .hangup = uart_hangup,
- .break_ctl = uart_break_ctl,
- .wait_until_sent= uart_wait_until_sent,
- #ifdef CONFIG_PROC_FS
- .read_proc = uart_read_proc, //proc入口读函数
- #endif
- .tiocmget = uart_tiocmget,
- .tiocmset = uart_tiocmset,
- };
这个声明采用了标记化结构初始化语法。这种写法是值得采用的,因为它使驱动程序在结构的定义发生变化时更具有可移植性,并且使代码更加紧凑且易读。标记化的初始化方法允许对结构成员进行重新排列。在某些场合下,将频繁被访问的成员放在相同的硬件缓存行上,将大大提高性能。
---LLD3
标记化结构初始化语法是ISO C99的用法
C Primer Plus第五版相关章节:
已知一个结构,定义如下:
- struct book
- {
- char title[MAXTITL];
- char author[MAXAUTL];
- float value;
- };
C99支持结构的指定初始化项目,其语法与数组的指定初始化项目近似。只是,结构的指定初始化项目使用点运算符和成员名(而不是方括号和索引值)来标识具体的元素。例如,只初始化book结构的成员vlaue,可以这样做:
- 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赋给成员vlaue,因为它在结构声明中紧跟在author成员之后。新的值0.25代替了早先的赋值18.90。
有关designated initializer的进一步信息可以参考C99标准的6.7.8节Initialization。
代码举例:
- #include <stdio.h>
- #include <stdlib.h>
- struct operators
- {
- void (*read1)(char *);
- void (*read2)(char *);
- void (*read3)(char *);
- int n;
- };
- void read1(char *data)
- {
- printf("read1: %s/n",data);
- }
- void read2(char *data)
- {
- printf("read2: %s/n",data);
- }
- void read3(char *data)
- {
- printf("read3: %s/n",data);
- }
- int main()
- { //传统的初始化方法
- //struct operators my_op = {read1, read2, read3, 100}; //所谓的标记化结构初始化语法
- struct operators my_op = {.read2 = read2,
- .read1 = read1,
- .read3 = read3,
- .n = 100};
- my_op.read1("wangyang");
- my_op.read2("wangyang");
- my_op.read3("wangyang");
- return 0;
- }
重点就在于main()函数中对my_op结构体的初始化语句,使用点加变量名进行初始化。用过Python的人会马上感觉这与关键字传参是多么的相似。
那它好处在哪里呢?
我想好处有三。
首先,标记传参不用理会参数传递的顺序,正如我上面的例子里表示的那样,我是先初始化了read2,然后再初始化了read1,程序员不用记忆参数的顺序;
再者,我们可以选择性传参,在传统C语言顺序传参中,如果你只想对第三个变量进行初始化,那么你不得不给第一个,第二个参数进行初始化,而有时候一个变量并没有和合适的默认值,而使用标记初始化法,你可以相当自由地对你有把握的参数进行初始化;
还有,扩展性更好,如果你要在该结构体中增加一个字段,传统方式下,为了考虑代码修改量,你最好将新添加的字段放在这个结构体的最后面,否则你将要面对大量而且无趣的修改,你可能觉得放在哪里没什么关系,但是我们都习惯了,姓名下面是性别,性别下面是年龄,接着是兴趣爱好,最后是事迹描述,如果年龄放在了最后面,难道不别扭么?
上面的例程为什么在VC++6.0中编译不同通过呢???
在bluedrum的空间中有篇名为《C版本差异 --- 结构处理差别》的第3点中讲到:
在标准C中(C89),结构标准初始化是用{}来初始化,在C99的版本,采用了可读性更强的标记化初始化,这在Linux内核和驱动中很为常见。
其中VC++6.0只支持C89初始化,GCC支持自己标记化或自己扩展初始化。
- struct name_str{
- int data;
- char name[120];
- int num;
- };
- /* 标记式初始化,注意顺序不同,并可缺省 */
- struct name_str str ={
- .num = 100;
- .name = "hxy";
- };
- /* C89 初始化 */
- struct name_str str2 =
- {
- 100,"Andrew Huang",-2
- };
- /* gcc 扩展初始化 */
- struct name_str str3 =
- {
- name:"bluedrum";
- data:-1
- }
- }
个人想编译以上代码,想下个C99编译器,在百度搜索 C99编译器,解决时间:2009-07-10 21:54回答是
“VC++ 2005 支持的是C89 而不是C99 这点可以在一次对VS2005的负责人的采访中看出来,他解释了为什么VS2005支持C89 而不支持C99 目前完全支持C99标准的编译器还不存在 支持部分C99标准的编译器也不多 做的最好的是GCC”
特定的初始化
标准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 };
- struct point p = { y: yvalue, x: xvalue };
"[index]"或".fieldname"就是指示符。在初始化共同体时,你也可以使用一个指示符(或不再使用的冒号语法),来指定共同体的哪个元素应该使用。比如:
- union foo { int i;double d; };
- union foo f = { .d = 4 };
你可以把这种命名元素的技术和连续元素的普通C初始化结合起来。每个没有指示符的初始化元素应用于数组或结构体中的下一个连续的元素。比如:
- int a[6] = { [1] = v1, v2, [4] = v4 };
等价于
- int a[6] = { 0, v1, v2, 0, v4, 0 };
- int whitespace[256]
- = { [' '] = 1, ['\t'] = 1, ['\h'] = 1,
- ['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };
- struct point ptarray[10] = { [2].y = yv2, [2].x = xv2, [0].x = xv0 };
这篇关于标记化结构初始化语法---结构体成员前加小数点的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!