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

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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

脏页的标记方式详解

脏页的标记方式 一、引言 在数据库系统中,脏页是指那些被修改过但还未写入磁盘的数据页。为了有效地管理这些脏页并确保数据的一致性,数据库需要对脏页进行标记。了解脏页的标记方式对于理解数据库的内部工作机制和优化性能至关重要。 二、脏页产生的过程 当数据库中的数据被修改时,这些修改首先会在内存中的缓冲池(Buffer Pool)中进行。例如,执行一条 UPDATE 语句修改了某一行数据,对应的缓

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使用了由[90]描述的第一个算法。开发者应该注意,由于数据点靠近包含的 Mat 元素的边界,返回的椭圆/旋转矩形数据

三色标记(Tri-color marking)

维基百科部分 原文 https://en.wikipedia.org/wiki/Tracing_garbage_collection#TRI-COLOR Because of these performance problems, most modern tracing garbage collectors implement some variant of the tri-color ma

C++语法知识点合集:11.模板

文章目录 一、非类型模板参数1.非类型模板参数的基本形式2.指针作为非类型模板参数3.引用作为非类型模板参数4.非类型模板参数的限制和陷阱:5.几个问题 二、模板的特化1.概念2.函数模板特化3.类模板特化(1)全特化(2)偏特化(3)类模板特化应用示例 三、模板分离编译1.概念2.模板的分离编译 模版总结 一、非类型模板参数 模板参数分类类型形参与非类型形参 非类型模板

Java基础回顾系列-第一天-基本语法

基本语法 Java基础回顾系列-第一天-基本语法基础常识人机交互方式常用的DOS命令什么是计算机语言(编程语言) Java语言简介Java程序运行机制Java虚拟机(Java Virtual Machine)垃圾收集机制(Garbage Collection) Java语言的特点面向对象健壮性跨平台性 编写第一个Java程序什么是JDK, JRE下载及安装 JDK配置环境变量 pathHe

C语言程序设计(选择结构程序设计)

一、关系运算符和关系表达式 1.1关系运算符及其优先次序 ①<(小于) ②<=(小于或等于) ③>(大于) ④>=(大于或等于 ) ⑤==(等于) ⑥!=(不等于) 说明: 前4个优先级相同,后2个优先级相同,关系运算符的优先级低于算术运算符,关系运算符的优先级高于赋值运算符 1.2关系表达式 用关系运算符将两个表达式(可以是算术表达式或关系表达式,逻辑表达式,赋值表达式,字符