C语言 在编译时初始化灵活数组成员



我正在尝试优化嵌入式应用程序的启动时和运行时,要做到这一点,我想在编译时初始化一些常量灵活数组成员。我发现一些帖子说你必须使用malloc,但从理论上讲,应该可以做到这一点,没有…

例如,我有:

typedef struct _foo_t {
foo_t *next;
int   num_bars;
bar_t bars[];
} foo_t __attribute((__packed__));

我有几百万个foo_t实例。然后,我有一个脚本来生成包含所有信息的头文件。所以我可以这样写:

const foo_t foo1 = {.next = &foo2, .num_bars = 2};
const bar_t foo1_bar1 = {...};
const bar_t foo1_bar2 = {...};
foo_t *const first_foo = &foo1;

但是,这样做的问题是编译器规范不能保证&foo1_bar1不保证在&foo1.bars[0]。我想知道是否有人知道强迫fooX_barY成员在内存中的正确位置。

注意我的目标是:

  • 通过避免不必要的malloc来减少启动时间
  • 通过将栏的通常与相应的foos
  • 放在相同的缓存页中来减少内存抖动
  • 通过不使用指向条的指针来减少内存需求

如果有人知道什么好技巧来做到这一点,我很乐意听听

GCC和clang似乎支持灵活数组的标准初始化器,所以如果你可以限制自己使用这些编译器,你只需要为const移除添加强制转换。初始化器不必使用指定成员,至少对于clang,经典的初始化器工作得很好。.

这个扩展实际上与数组初始化器的C语法非常一致,如果可以从初始化器中确定数组的长度,则可以省略数组的长度。

对于不支持这种语法的编译器,这里有一个技巧来实现你想要的:

//----------------
// you can move these to foo.h
typedef int bar_t;
typedef struct foo_t {
struct foo_t *next;
char name[8];
int num_bars;
bar_t bars[];
} foo_t;
extern foo_t * const first_foo;
extern foo_t * const second_foo;
//----------------
// The definitions can be generated automatically into a separate module
// disable warnings cf: https://stackoverflow.com/a/55877109/4593267
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma clang diagnostic ignored "-Wcast-qual"
// Trick for VLA initialization
#define foo_t(n)  struct { foo_t *next; char name[8]; int num_bars; bar_t bars[n]; }
// using a classic structure initializers
static const foo_t(2) foo2 = { 0,              "foo2", 2, { 1, 2 }};
static const foo_t(1) foo1 = { (foo_t *)&foo2, "foo1", 1, { 42 }};
foo_t * const first_foo = (foo_t *)&foo1;
// using a compound literal
foo_t * const second_foo = (foo_t *)&(foo_t(3)){ 0, "foo3", 3, { 10, 20, 30 }};
// using gcc / clang flexible array initializer
#pragma clang diagnostic ignored "-Wgnu-flexible-array-initializer"
static foo_t third_foo = { 0, "foo4", 2, { 1, 2 }};
//----------------
// Test framework
#include <stdio.h>
void foo_print(const char *name, foo_t *p) {
printf("%s: {n", name);
for (; p; p = p->next) {
printf("  { "%s", %d, { ", p->name, p->num_bars);
for (int i = 0; i < p->num_bars; i++)
printf("%d, ", p->bars[i]);
printf("}},n");
}
printf("}n");
}
int main() {
foo_print("first_foo", first_foo);
foo_print("second_foo", second_foo);
foo_print("third_foo", &third_foo);
return 0;
}

输出:

first_foo: {
{ "foo1", 1, { 42, }},
{ "foo2", 2, { 1, 2, }},
}
second_foo: {
{ "foo3", 3, { 10, 20, 30, }},
}
third_foo: {
{ "foo4", 2, { 1, 2, }},
}

您尝试使用相关变量的某些排序很容易出错,因为编译器不需要在您想要的位置分配变量。

使用静态变量而不是动态分配内存的唯一方法是将该灵活数组的成员直接添加到变量的初始化式中:

const foo_t foo1 =
{
.next = &foo2,
.num_bars = 2,
.bars =
{ [0] = {...},
[1] = {...}
}
};

专用初始化器是可选的。

至少对于GCC,这应该可以工作。

不幸的是,不可能使用常见的(sizeof(arr)/sizeof(arr[0]))技巧来获取数组中的元素数量:

foo_t foo1 =
{
.next = &foo2,
.bars =
{ [0] = 1,
[1] = 2
},
.num_bars = sizeof(foo1.bars)/sizeof(foo1.bars[0]),
};
test.c:21:21: error: invalid application of ‘sizeof’ to incomplete type ‘bar_t[]’ {aka ‘int[]’}
21 |   .num_bars = sizeof(foo1.bars)/sizeof(foo1.bars[0]),
|                     ^

相关内容

  • 没有找到相关文章

最新更新