3

在一个嵌入式项目中,我使用了一个库,该库提供了一个用于初始化结构的宏。这提供了合理的默认值,但默认值取决于其他参数。我想覆盖这个指定初始化器的一个或多个值,因为之后初始化这些值会产生开销。

理想情况下,我不想复制粘贴所有宏,因为我必须管理第三方代码。如果库更改它的默认值,我也不想这样做。

有没有一种组合或覆盖指定初始化器的方法,所以没有开销?代码必须符合 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;
}
4

2 回答 2

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因此此技巧没有额外的开销。

于 2016-02-20T18:43:03.583 回答
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) 任何被覆盖的子对象的初始化器,因此不用于初始化该子对象可能根本不会被评估。

也就是说编译器需要使用最后指定的初始化器,并且可以在没有任何开销的情况下实现这个解决方案。

于 2016-02-20T18:55:39.913 回答