GCC 数组默认填充值

GCC array default fill value

一言以蔽之

我最近发现了 gcc 的一个扩展,我发现它非常酷而且很有帮助。这允许您指定初始化元素在数组中的索引。所以而不是

static const uint8_t array[4] = {
    2,
    1,
    3,
    4
};

能写

static const uint8_t array[4] = {
    [1] = 1,
    [0] = 2,
    [2] = 3,
    [3] = 4
};

然而,我通常有一个数组,我有一个或两个需要不同的特定值,而数组的其余部分设置为某个默认值。我希望能够在初始化程序中完成这一切;像

static const uint8_t array[4] = {
    [1]               = 1,
    [everything else] = 0
};

有谁知道这样做的好方法吗?我想出的唯一方法(这似乎有点老套)是定义一个 EVERYTHING_ELSE 宏,它依赖于相同的值和数组长度——像这样:

#define VALUE                            (8)
#define DEFAULT                          (1)
#define INDEX                            (2)
#define ARRAY_LENGTH                     (4)
#define EVERYTHING_ELSE(n, index, len)   (((index) + (n)) % (len))

static const uint8_t array[ARRAY_LENGTH] = {
    [EVERYTHING_ELSE(1, INDEX, ARRAY_LENGTH)] = DEFAULT,
    [EVERYTHING_ELSE(2, INDEX, ARRAY_LENGTH)] = DEFAULT,
    [EVERYTHING_ELSE(3, INDEX, ARRAY_LENGTH)] = DEFAULT,
    [INDEX]                                   = VALUE;
}; 

一些背景知识

我是做嵌入式系统编程的,经常就是配置外设。在这种特殊情况下,数组的每个元素可能对应于外围设备中的一个通道。如果我正在为不同的板构建一个项目,引脚分配略有不同,我可能会在不同的情况下使用不同的通道。为了帮助加强正确性和可移植性,我更喜欢在一个地方定义这个值,并编写依赖于这个值的代码。

此外,我需要确保停用该特定外围设备中的所有其他通道。在上面的例子中,我有一个简单的整数数组,但通常情况更复杂(它是一个初始化结构数组,或类似的东西)。我知道默认情况下静态数组会被初始化为零,但并不总是零对应未初始化的情况;此外,我认为依赖库常量作为正确运行代码的特定值是不好的形式。

我可以使数组成为非常量并在运行时进行初始化,但在嵌入式系统中这可能是个问题。如果您只有 6k 的 RAM,则有强烈的动机将编译时可以确定的所有内容都放在闪存中,为实际需要在运行时变化的内容留出最大 RAM space。

这不是 gcc 的特性,而是自 C99 以来的标准功能,称为 designated initializer。 struct 字段也是可能的(这与强调 imo 更相关)。

请注意,标准只允许指定单个索引(使用常量表达式),而不是范围,即 gcc 扩展。也没有标准方法将 "all other" 字段设置为 0 以外的值([=25= 为 0.0,指针为 NULL)。

根据标准的定义,未明确初始化的 fields/elements 设置为 0。但是,如果您有一个较大的数组,其中少数字段设置为非 0,则应从您的代码中明确设置这些字段(如果对象不是 const 当然)。原因是 0 初始化的全局对象不消耗 ROM/Flash/file-space,而非零初始化对象的初始化程序必须显式存储 - 完全。对于嵌入式系统,这显然不是一个好主意。

如果你必须建立一个大数组,用一个小程序创建一个带有数组的C源代码文件可能是个好主意(我使用Python,因为它非常简单的)。然后,此文件可以 #include 在您的程序代码中(如果 static)或 compiled/linked 您的程序与任何其他源代码一样。