在嵌入式项目中,我使用一个提供宏来初始化结构的库。这提供了合理的默认值,但默认值取决于其他参数。我想覆盖此指定初始值设定项的一个或多个值,因为之后初始化值会产生开销。
理想情况下,我不想复制粘贴所有宏,因为我必须管理第三方代码。如果图书馆更改了默认设置,我也不想这样做。
有没有办法合并或覆盖designated initializers,所以没有开销?代码必须符合C99并且可移植。
用于演示此问题的一些示例代码:
#if SITUATION
#define LIBRARY_DEFAULTS \
{ \
.field_a = 1, \
.field_b = 2, \
.field_c = 3 \
}
#else
#define LIBRARY_DEFAULTS \
{ \
.field_a = 100, \
.field_b = 200, \
.field_c = 300, \
.field_d = 400, \
.field_e = 500 \
}
#endif
/* The following is what I want (or similar), but (of course) doesn't
work. */
// #define MY_DEFAULTS = LIBRARY_DEFAULTS + { .field_a = 100 }
int main(void) {
/* The exact definition of something also depends on situation. */
struct something library_thing = LIBRARY_DEFAULTS;
/* This generates overhead, and I want to avoid this. It is certain
that the field exists. */
library_thing.field_a = 100;
}
答案 0 :(得分:1)
您可以将library_thing
包装在外部结构中,并从外部结构的初始化程序执行覆盖:
#include <stdio.h>
struct foo {
int a,b,c;
};
#define FOO_DEFAULTS { .a = 1, .b = 2, .c = 3 }
int main() {
struct {
struct foo x;
} baz = {
.x = FOO_DEFAULTS,
.x.a = 4,
};
printf("%d\n", baz.x.a); // prints 4
}
事实上,你甚至可以做到
.x = FOO_DEFAULTS,
.x = {.a = 4},
如果你需要真正“合并”两个初始化器。
这对Clang(7.0.2)编译很好,但在-Winitializer-overrides
下生成警告。检查生成的代码确认结构已使用4, 2, 3
初始化,因此此技巧不会产生额外开销。
答案 1 :(得分:1)
这是一个可能的解决方案。首先从宏
中删除大括号#define LIBRARY_DEFAULTS .a=1, .b=2, .c=3
然后,对于默认值正常的变量,将宏括在大括号中
struct something standard = { LIBRARY_DEFAULTS };
对于需要调整默认值的变量,添加一些额外的初始化程序
struct something tweaked = { LIBRARY_DEFAULTS, .a=100 };
为什么这样做? C规范的第6.7.9节讨论了在初始化列表中使用指示符,并且有关于多次指定同一个指示符的说法:
19初始化应在初始化程序列表顺序中进行,每个顺序 初始化程序为特定的子对象提供覆盖任何子对象 先前列出的同一子对象的初始化程序; 151) 所有未明确初始化的子对象应为 隐式初始化与具有静态存储的对象相同 持续时间。
并注释151说明
151)子对象的任何初始值设定项被覆盖,因此不被覆盖 用于初始化该子对象可能根本不会被评估。
也就是说编译器需要使用最后指定的初始化程序,而可能实现此解决方案而不会产生任何开销。