在C语言中初始化变量的最有效方法是什么?

3

我正在开发一个嵌入式应用程序,其中RAM可用性非常有限。在分析内存消耗时,我发现bss段相当大,并且进一步研究发现这可能是由于变量初始化不足造成的。

以下是我通常会做的事情的示例。例如,假设我有这个结构体:

typedef struct
{
    float time;
    float value;
} pair_t;

我通常不会在声明变量时初始化,而是像这样做:

pair_t test()
{
    pair_t ret;
    ret.time = 0;
    ret.value = 0;
    return ret;
}

我应该像这样一次性完成所有工作吗?还是没有区别?
pair_t test()
{
    pair_t ret = (pair_t)
    {
        .time = 0,
        .value = 0
    };
    return ret;
}

对于现代编译器来说,这不应该有任何影响。然而,如果你的结构体非常大,bzero(ptr, size) 可能会更有优势。 - mvp
你可以尝试使用(返回)复合字面量:pair_t test(void) { return (pair_t){ 0.0, 0.0 }; }(或者 0.0f,但我想如果与 0.0 相比较的话不会有任何区别)。测量或查看生成的汇编代码是查看符号表示法是否有差异的最佳方法。 - Jonathan Leffler
我有点喜欢定义初始值(例如“#define PAIR_T_INITIALIZER {0.0,0.0} ”),这样初始化程序和结构体就在同一个文件中并排放置(更易于维护,在向结构添加新字段时更难出错)。在需要时,使用(例如)“pair_t ret = PAIR_T_INITIALIZER;”。但这与内存消耗无关。 - Brendan
为了节省内存,每个“pair_t”将花费您“sizeof(pair_t)”个字符,无论您做什么或如何初始化它;除非一切都永远初始化为零。 - Brendan
你确定这个变量没有声明为 static 吗?否则它不会对 bss 段的大小产生贡献。当然,除非编译器在 bss 段中创建一个复合字面量,然后插入一个 memcpy。 - Ajay Brahmakshatriya
2个回答

3
在分析内存消耗时,我发现bss段相当大,进一步研究表明这可能是由于变量初始化不足造成的。一个大的bss段意味着你的应用程序拥有很多全局和/或静态变量(或者某些非常大的变量)。这些变量位于bss段中,因为它们被初始化为零,或者未被初始化——但将它们初始化为非零值只会使它们移到数据段中,数据段也驻留在内存中。如果这些变量的内存使用率成为问题,您需要使用更少的变量。改变它们的初始化方式是没有帮助的。
话虽如此,在这里处理的变量根本不在bss段中,因为它是一个局部变量。无论在声明变量时初始化还是使用赋值语句显式地初始化变量之间都没有任何区别;任何明智的编译器都将为两者生成完全相同的代码。

1
虽然我大多数情况下同意这个变量不应该对bss段大小产生影响,但是有可能编译器(大多数情况下禁用优化)会在bss段中创建一个复合字面量,然后插入memcpy,因为OP实际上创建了一个复合字面量。 - Ajay Brahmakshatriya
@AjayBrahmakshatriya:如果你禁用了优化编译,那么你就不再关心代码或数据的大小。 - Jonathan Leffler

1

TL;DR

如果你只关心清零结构体,最少的写法是在初始化结构体时使用= {0},而且这很可能也会产生最佳代码,除非你不想初始化所有成员;如果不想初始化所有成员,则必须使用策略1,即对成员进行赋值。


一种好的实现方式可以注意到每个变量具有相同的副作用,并在启用优化时为每个选项生成相同的代码。而一种不好的实现方式可能无法做到这一点,就像我们人类更容易看出0是零,而不是看出 e^iπ + 1一样。请注意。
pair_t test()
{
    pair_t ret = (pair_t)
    {
        .time = 0,
        .value = 0
    };
    return ret;
}

此外,它还创建了一个未使用的类型为pair_t的复合字面量。但是,糟糕的编译器可能会保留它。适当的初始化代码是:
pair_t ret = {
    .time = 0,
    .value = 0
};

没有那个看起来像铸造的东西。

使用简单的零初始化器甚至可能更便宜。

pair_t ret = { 0 };

这应该产生相同的效果,但更明确地为零。

至少MSVC似乎在没有启用优化的情况下被欺骗了,参见this one

但是当启用优化(-O3)时,GCC 9.1 x86-64将生成

test:
        pxor    xmm0, xmm0
        ret

对于所有选项,但这又是一个实现质量问题,对于质量较差的编译器(MSVC 19.14),可能仅为默认初始化程序{0}生成最短的代码:

ret$ = 8
test    PROC                                            ; COMDAT
        xor     eax, eax
        mov     QWORD PTR ret$[rsp], rax
        ret     0
test    ENDP

如果你将其与使用严格正确的{0.0f, 0.0f}进行比较:

test    PROC                                            ; COMDAT
        xorps   xmm1, xmm1
        xorps   xmm0, xmm0
        unpcklps xmm0, xmm1
        movq    rax, xmm0
        ret     0
test    ENDP

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