标记化结构初始化语法---结构体成员前加小数点

2023-12-01 19:48

本文主要是介绍标记化结构初始化语法---结构体成员前加小数点,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对结构体

[cpp] view plain copy print ?
  1. struct a {

  2. int b;

  3. int c;

  4. }

有几种初始化方式:

[cpp] view plain copy print ?
  1. struct a a1 = {
  2. .b = 1,
  3. .c = 2
  4. };

或者

[cpp] view plain copy print ?
  1. struct a a1 = {
  2. b:1,
  3. c:2
  4. }

或者

[cpp] view plain copy print ?
  1. struct a a1 = {1, 2};

内核喜欢用第一种,使用第一种和第二种时,成员初始化顺序可变

标记化结构初始化语法

在Linux2.6内核中对结构体的定义形式发生了变化,不再支持原来的定义形式。

[cpp] view plain copy print ?
  1.  static struct tty_operations uart_ops =
  2.  {
  3.            .open = uart_open,//串口打开
  4.            .close = uart_close,//串口关闭
  5.            .write = uart_write,//串口发送
  6.            .put_char = uart_put_char,//...
  7.            .flush_chars = uart_flush_chars,
  8.          .write_room = uart_write_room,
  9.         .chars_in_buffer= uart_chars_in_buffer,
  10.         .flush_buffer = uart_flush_buffer,
  11.          .ioctl = uart_ioctl,
  12.          .throttle = uart_throttle,
  13.          .unthrottle = uart_unthrottle,
  14.           .send_xchar = uart_send_xchar,
  15.          .set_termios = uart_set_termios,
  16.          .stop = uart_stop,
  17.          .start = uart_start,
  18.         .hangup = uart_hangup,
  19.         .break_ctl = uart_break_ctl,
  20.        .wait_until_sent= uart_wait_until_sent,
  21.        #ifdef CONFIG_PROC_FS
  22.        .read_proc = uart_read_proc, //proc入口读函数
  23.        #endif
  24.       .tiocmget = uart_tiocmget,
  25.       .tiocmset = uart_tiocmset,
  26.       };


这个声明采用了标记化结构初始化语法这种写法是值得采用的,因为它使驱动程序在结构的定义发生变化时更具有可移植性,并且使代码更加紧凑且易读。标记化的初始化方法允许对结构成员进行重新排列。在某些场合下,将频繁被访问的成员放在相同的硬件缓存行上,将大大提高性能

---LLD3

标记化结构初始化语法是ISO C99的用法

C Primer Plus第五版相关章节:

已知一个结构,定义如下:

[cpp] view plain copy print ?
  1. struct book
  2. {
  3. char title[MAXTITL];
  4. char author[MAXAUTL];
  5. float value;
  6. };

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

[cpp] view plain copy print ?
  1. struct book surprise = { .value = 10.99 };

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

[cpp] view plain copy print ?
  1. struct book gift = {

  2. .value = 25.99,
  3. .author = "James Broadfool",
  4. .title = "Rue for the Toad"
  5. };

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

[cpp] view plain copy print ?
  1. struct book gift = {

  2. .value = 18.90,
  3. .author = "Philionna pestle",
  4. 0.25
  5. };


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

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

代码举例:

[cpp] view plain copy print ?
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. struct operators
  4. {
  5. void (*read1)(char *);
  6. void (*read2)(char *);
  7. void (*read3)(char *);
  8. int n;
  9. };

  10. void read1(char *data)
  11. {
  12. printf("read1: %s/n",data);
  13. }
  14. void read2(char *data)
  15. {
  16. printf("read2: %s/n",data);
  17. }
  18. void read3(char *data)
  19. {
  20. printf("read3: %s/n",data);
  21. }

  22. int main()
  23. { //传统的初始化方法
  24. //struct operators my_op = {read1, read2, read3, 100}; //所谓的标记化结构初始化语法
  25. struct operators my_op = {.read2 = read2,
  26. .read1 = read1,
  27. .read3 = read3,
  28. .n = 100};
  29. my_op.read1("wangyang");
  30. my_op.read2("wangyang");
  31. my_op.read3("wangyang");
  32. return 0;
  33. }


重点就在于main()函数中对my_op结构体的初始化语句,使用点加变量名进行初始化。用过Python的人会马上感觉这与关键字传参是多么的相似。

那它好处在哪里呢?

我想好处有三。

首先,标记传参不用理会参数传递的顺序,正如我上面的例子里表示的那样,我是先初始化了read2,然后再初始化了read1,程序员不用记忆参数的顺序;

再者,我们可以选择性传参,在传统C语言顺序传参中,如果你只想对第三个变量进行初始化,那么你不得不给第一个,第二个参数进行初始化,而有时候一个变量并没有和合适的默认值,而使用标记初始化法,你可以相当自由地对你有把握的参数进行初始化;

还有,扩展性更好,如果你要在该结构体中增加一个字段,传统方式下,为了考虑代码修改量,你最好将新添加的字段放在这个结构体的最后面,否则你将要面对大量而且无趣的修改,你可能觉得放在哪里没什么关系,但是我们都习惯了,姓名下面是性别,性别下面是年龄,接着是兴趣爱好,最后是事迹描述,如果年龄放在了最后面,难道不别扭么?

上面的例程为什么在VC++6.0中编译不同通过呢???

在bluedrum的空间中有篇名为《C版本差异 --- 结构处理差别》的第3点中讲到:

在标准C中(C89),结构标准初始化是用{}来初始化,在C99的版本,采用了可读性更强的标记化初始化,这在Linux内核和驱动中很为常见。

其中VC++6.0只支持C89初始化,GCC支持自己标记化或自己扩展初始化。

[cpp] view plain copy print ?
  1. struct name_str{
  2. int data;
  3. char name[120];
  4. int num;
  5. };
  6. /* 标记式初始化,注意顺序不同,并可缺省 */
  7. struct name_str str ={
  8. .num = 100;
  9. .name = "hxy";

  10. };

  11. /* C89 初始化 */
  12. struct name_str str2 =
  13. {
  14. 100,"Andrew Huang",-2
  15. };

  16. /* gcc 扩展初始化 */
  17. struct name_str str3 =
  18. {
  19. name:"bluedrum";
  20. data:-1
  21. }
  22. }

个人想编译以上代码,想下个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] = "。比如:

[cpp] view plain copy print ?
  1. int a[6] = { [4] = 29, [2] = 15 };

相当于:

[cpp] view plain copy print ?
  1. int a[6] = { 0, 0, 15, 0, 29, 0 };

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

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

[html] view plain copy print ?
  1. int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
如果其中的值有副作用,这个副作用将只发生一次,而不是范围内的每次初始化一次。

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

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

[cpp] view plain copy print ?
  1. struct point { int x, y; };
和下面的初始化:
[cpp] view plain copy print ?
  1. struct point p = { .y = yvalue, .x = xvalue };
等价于:
[cpp] view plain copy print ?
  1. struct point p = { xvalue, yvalue };
另一有相同含义的语法是“.fieldname:”,不过从GCC 2.5开始废除了,就像这里所示:
[cpp] view plain copy print ?
  1. struct point p = { y: yvalue, x: xvalue };

"[index]"或".fieldname"就是指示符。在初始化共同体时,你也可以使用一个指示符(或不再使用的冒号语法),来指定共同体的哪个元素应该使用。比如:
[cpp] view plain copy print ?
  1. union foo { int i;double d; };
  2. union foo f = { .d = 4 };
将会使用第二个元素把4转换成一个double类型来在共同体存放。相反,把4转换成union foo类型将会把它作为整数i存入共同体,既然它是一个整数。(参考5.24节共同体类型转换)

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

[cpp] view plain copy print ?
  1. int a[6] = { [1] = v1, v2, [4] = v4 };

等价于

[cpp] view plain copy print ?
  1. int a[6] = { 0, v1, v2, 0, v4, 0 };
当下标是字符或者属于enum类型时,标识数组初始化语句的元素特别有用。例如:

[cpp] view plain copy print ?
  1. int whitespace[256]
  2. = { [' '] = 1, ['\t'] = 1, ['\h'] = 1,
  3. ['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };
你也可以在"="前面协商一系列的".fieldname"和"[index]"指示符来指定一个要初始化的嵌套的子对象;这个列表是相对于和最近的花括号对一致的子对象。比如,用上面的struct point声明:

[cpp] view plain copy print ?
  1. struct point ptarray[10] = { [2].y = yv2, [2].x = xv2, [0].x = xv0 };
如同一个成员被初始化多次,它将从最后一次初始化中取值。如果任何这样的覆盖初始化有副作用,副作用的发生与否是非指定的。目前,GCC会舍弃它们并产生一个警告。

这篇关于标记化结构初始化语法---结构体成员前加小数点的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/442493

相关文章

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

Python+PyQt5实现文件夹结构映射工具

《Python+PyQt5实现文件夹结构映射工具》在日常工作中,我们经常需要对文件夹结构进行复制和备份,本文将带来一款基于PyQt5开发的文件夹结构映射工具,感兴趣的小伙伴可以跟随小编一起学习一下... 目录概述功能亮点展示效果软件使用步骤代码解析1. 主窗口设计(FolderCopyApp)2. 拖拽路径

C++类和对象之初始化列表的使用方式

《C++类和对象之初始化列表的使用方式》:本文主要介绍C++类和对象之初始化列表的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C++初始化列表详解:性能优化与正确实践什么是初始化列表?初始化列表的三大核心作用1. 性能优化:避免不必要的赋值操作2. 强

SpringIOC容器Bean初始化和销毁回调方式

《SpringIOC容器Bean初始化和销毁回调方式》:本文主要介绍SpringIOC容器Bean初始化和销毁回调方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录前言1.@Bean指定初始化和销毁方法2.实现接口3.使用jsR250总结前言Spring Bea

mysql递归查询语法WITH RECURSIVE的使用

《mysql递归查询语法WITHRECURSIVE的使用》本文主要介绍了mysql递归查询语法WITHRECURSIVE的使用,WITHRECURSIVE用于执行递归查询,特别适合处理层级结构或递归... 目录基本语法结构:关键部分解析:递归查询的工作流程:示例:员工与经理的层级关系解释:示例:树形结构的数

Spring实现Bean的初始化和销毁的方式

《Spring实现Bean的初始化和销毁的方式》:本文主要介绍Spring实现Bean的初始化和销毁的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Bean的初始化二、Bean的销毁总结在前面的章节当中介绍完毕了ApplicationContext,也就

Java使用Stream流的Lambda语法进行List转Map的操作方式

《Java使用Stream流的Lambda语法进行List转Map的操作方式》:本文主要介绍Java使用Stream流的Lambda语法进行List转Map的操作方式,具有很好的参考价值,希望对大... 目录背景Stream流的Lambda语法应用实例1、定义要操作的UserDto2、ListChina编程转成M

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中