如何初始化具有可变索引的结构体数组

5
我有以下结构:
typedef struct
{
    int a;
    int b;
    int c;
} my_struct;

在另一个文件中,我已经声明了一个my_struct类型的变量,如下所示。

my_struct strct_arr[MAX];

MAX是一个可配置值的宏,它是18的倍数(例如18或36或54等),可能会达到18 * n倍。

我必须用 {0xff,0,0} 初始化结构体。那么如何用我的初始值初始化整个结构体数组 my_struct strct_arr[MAX];,而不使用任何循环。

我期望的输出如下:

my_struct strct_arr[MAX]={
    {0xff,0,0},
    {0xff,0,0},
    {0xff,0,0},
    {0xff,0,0},
    …
};

但是如果不知道MAX的值,该如何初始化呢?


1
你能看一下这里吗?https://dev59.com/JmMk5IYBdhLWcg3w2BXJ - Jayesh Bhoi
如果只有18、36或54是可选项,那么我可能会使用一些#ifdef来编写它。 - j_kubik
http://www.cplusplus.com/reference/cstring/memset/?kw=memset - xmojmr
@xmojmr memset只能用于设置整数值。 - wilbeibi
@Wilbeibi: 实际上,memset 只能用于使用 char 值填充内存。该参数声明为 int 类型,纯粹是出于历史原因。 - AnT stands with Russia
显示剩余2条评论
5个回答

4

4
是的,这可以使用C预处理器实现!
#include <stdio.h>
#include <boost/preprocessor/repetition/repeat.hpp>

#define INITS(z, n, t) { 0xFF, 0, 0 },
#define REP(item, n) BOOST_PP_REPEAT(n, INITS, item)

#define MAX 123

typedef struct { int a,b,c; } my_struct;

my_struct ms[] = { REP(, MAX) };

int main()
{
    // Check it worked
    printf("%d\n", (int)(sizeof ms / sizeof *ms));
}

注意:boost是一个C++库,然而boost/preprocessor只使用两种语言都通用的预处理器功能。如果您的实现默认不允许此#include,可以从boost源代码中找到repeat.hpp的副本。
另外,BOOST_PP_REPEAT默认最大为256。如果您的MAX比这个大,可以编辑repeat.hpp以允许更大的值,从那里开始应该很明显该怎么做。
注意:此帖子描述了一种递归宏的系统,不需要与repeat.hpp使用相同类型的实现,但我无法使其正常工作。
来源:此帖子

我知道一定有一个预处理器的解决方案。问题始于 #define MAX *something*,这是一个由预处理器引起的问题。解决方案也必须从那里出现。;) 正如我所怀疑的那样,它令人讨厌(但我无法移开目光)!+1 不让我失望。 :) - DavidO

3

嗯,标准C语言中没有直接且立即的语法来指定一个初始化器达到你想要的效果。如果你希望用0来初始化整个东西,则无论大小,= { 0 } 都可以工作,但是0xff就完全不同了。GCC编译器支持一种非标准扩展,在这种情况下可以使用(有关详细信息,请参见Sanket Parmar的答案),但遗憾的是这不是标准的。

还有一种非标准的memcpy方法,有时用于填充具有重复模式的内存区域。在你的情况下,它应该如下所示:

my_struct strct_arr[MAX] = { { 0xff, 0, 0 } };
memcpy(strct_arr + 1, strct_arr, sizeof strct_arr - sizeof *strct_arr);

但是这种方法是一个hack,因为它依赖于memcpy按字节逐个复制,并且严格按照从较小的内存地址到较大的内存地址的顺序进行操作。但是,语言规范并不保证这一点。如果你想要"合法化"这个技巧,你需要编写自己的版本my_memcpy,专门按照这种方式(按字节、从左到右)工作,并使用它代替原来的memcpy。当然,这实际上是一个循环解决方案,不完全基于初始化程序语法。

即使这个memcpy“工作”,那也是因为memcpy的实现中有一个循环,所以通过在自己的代码中显式地放置循环,你不会失去任何性能。 - M.M

2

以下是对Jonathan Leffler解决方案的改写:

struct my_struct { char c, int a; int b; }

#define MAX 135

#define INIT_X_1     { 0xff, 0, 0 }
#define INIT_X_2     INIT_X_1, INIT_X_1
#define INIT_X_4     INIT_X_2, INIT_X_2
#define INIT_X_8     INIT_X_4, INIT_X_4
#define INIT_X_16    INIT_X_8, INIT_X_8
#define INIT_X_32    INIT_X_16, INIT_X_16
#define INIT_X_64    INIT_X_32, INIT_X_32
#define INIT_X_128   INIT_X_64, INIT_X_64

struct my_struct strct_arr[MAX] =
{
#if (MAX & 1)
    INIT_X_1,
#endif
#if (MAX & 2)
    INIT_X_2,
#endif
#if (MAX & 4)
    INIT_X_4,
#endif
#if (MAX & 8)
    INIT_X_8,
#endif
#if (MAX & 16)
    INIT_X_16,
#endif
#if (MAX & 32)
    INIT_X_32,
#endif
#if (MAX & 64)
    INIT_X_64,
#endif
#if (MAX & 128)
    INIT_X_128,
#endif
};

Wildplasser先生,这是我经过一番努力后尝试的方法。但问题在于,我无法将初始化限制在54、62甚至128之内。很难想象用户配置会变化到什么程度,从而得出最大数值。因此,我创建了一个Perl脚本,它将根据配置的最大值生成一个宏。这样,在运行时就不会使C代码负担过重。 - PSR
嗯,将这个解决方案扩展到512、1024等规模并不难。每次翻倍只需要额外几行(四行)代码。但是代码生成始终是一个选项。 - wildplasser

0

为了多样性考虑,既然您知道数组将是18的倍数,您可以使用类似以下的代码:

#define INIT_X_1     { 0xff, 0, 0 }
#define INIT_X_3     INIT_X_1, INIT_X_1, INIT_X_1
#define INIT_X_9     INIT_X_3, INIT_X_3, INIT_X_3
#define INIT_X_18    INIT_X_9, INIT_X_9

my_struct strct_arr[MAX] =
{
    INIT_X_18,
#if MAX > 18
    INIT_X_18,
#if MAX > 36
    INIT_X_18,
#endif
#endif
};

这个方案不需要 C99 支持(甚至可以在早期版本的 C 中使用),也不需要 GCC 扩展或 Boost 预处理库。在其他方面,其他解决方案更好。


我看到你已经考虑了54次。如果我的数组大小是64,那么结构值是否会初始化64次呢?如果我需要继续到最大值,即可配置值,你有什么建议吗? - PSR
如果你有一个上限,可以使用wildplassergeneralization来解决我的问题。否则,你需要使用像你在他的答案下面评论中提到的额外语言机制。我根据你在问题中提到的18的倍数来定制了我的答案。如果失败了,考虑使用单个初始化器并在运行时将其复制到数组中。相比你要做的其他事情,这不会花费太多时间。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接