当前位置: 首页 > 知识库问答 >
问题:

对于采用常量结构的函数,编译器是否不优化函数体?

钱澄邈
2023-03-14

我有以下一段代码:

#include <stdio.h>

typedef struct {
    bool some_var;
} model_t;

const model_t model = {
    true
};

void bla(const model_t *m) {
    if (m->some_var) {
        printf("Some var is true!\n");
    }
    else {
        printf("Some var is false!\n");
    }
}

int main() {
    bla(&model);
}

我可以想象编译器具有消除bla()函数中的ore子句所需的所有信息。调用该函数的唯一代码路径来自main,它接受constmodel_t,因此它应该能够确定该代码路径未被使用。但是:

GCC 12.2中,我们看到第二部分链接到。

如果我内联函数,这将消失:

我在这里错过了什么?有没有办法让编译器做一些更智能的工作?这发生在带有 -O3 和 -O 的 C 和 C 中

共有3个答案

淳于思淼
2023-03-14

调用该函数的唯一代码路径来自main

GCC无法知道这一点,除非您使用<code>-fwhole程序-flto(链接时间优化)告诉GCC。否则,它必须假设另一个编译单元中的某个静态构造函数可以调用它。(可能包括在共享库中,但您链接的另一个.cpp可以做到这一点。)。

// another .cpp
typedef struct {  bool some_var; } model_t;
void bla(const model_t *m);              // declare the things from the other .cpp

int foo() {
    model_t model = {false};
    bla(&model);
    return 1;
}
int some_global = foo();  // C++ only: non-constant static initializer.

与main相同的编译单元中的这些行的Goldbolt上的示例,表明它输出两个有些var是假的!然后有些var是真的!,而无需更改main的代码。

ISO C没有简单的方法来执行初始化代码,但GNU C(特别是GCC)有方法让代码在启动时运行,而不是由main调用。这甚至适用于共享库。

使用-fwhole程序,适当的优化将根本不为其发出定义,因为它已经内联到main中的调用站点中。类似于<code>内联</code>(在C中,另一个编译单元中的任何其他调用方都可以看到其自己的函数定义)或<code>静态</code>(此编译单元的私有)。

main内部,它在不断传播后优化了分支。如果您运行程序,实际上不会执行任何分支;没有什么调用函数的独立定义。

函数的独立定义不知道m的唯一可能值是

只有< code>-fPIC会强制编译器考虑符号插入的可能性,因此< code>const model_t model的定义不是在(动态)链接后生效的定义。但是你是在为一个可执行文件而不是库编译代码。(通过赋予全局变量“隐藏”可见性,< code > _ _ attribute _ _((visibility(" hidden ")),或者使用< code>-fvisibility=hidden将其设为默认值,可以禁用全局变量的符号插入)。

狄信然
2023-03-14

编译器确实消除了main中的内联函数中的else路径。您混淆了全局函数,该函数无论如何都不会被调用,最终将被链接器丢弃。

如果您使用-fole程序标志让编译器知道没有其他文件将被链接,则该未使用的段将被丢弃:

[参见在线]

此外,您可以使用< code>static或< code>inline关键字来实现类似的功能。

籍辰沛
2023-03-14

编译器无法优化else路径,因为目标文件可能与任何其他代码链接。如果函数是静态的,或者使用整个程序优化,则情况会有所不同。

 类似资料:
  • 问题内容: 问题说明了一切。有人知道以下内容吗? …被优化成? 编译器会这样做吗?(我的兴趣在于GCC)。在某些情况下会这样做吗?在其他情况下却不会呢? 我真的很想知道,因为每次我写一个像这样可以优化的除法运算时,我都会花些精力思考是否浪费一秒钟宝贵的时间去做一个足以满足需要的除法运算。 问题答案: 即使g++ -O0(是,-O0!),也会发生这种情况。您的函数编译为: 请注意shrq $6,右移

  • 问题内容: 行给出了编译错误。 为何不允许这样做的任何特定原因?如何使用数组常量初始化String数组? 编辑:谢谢大家的回答。现在,我很清楚什么是允许的,什么是不允许的。但是我能问你 为什么 不允许这样做吗? 仔细搜索一下之后,我发现了这个链接,在其中,被告知像这样的编码使编译器不明确- 宠物应该是String数组还是Objects数组。但是,从声明中可以很好地看出它是一个String数组,对吗

  • 考虑下面的代码片段 通过-O3优化,最新的gcc和clang版本没有优化指向包装器的指针、指向底层函数的指针。参见第22行的组装: 在后面的+中,编译器将指针放置到,而不只是。 编辑2。同样模式的更简单的例子: gcc 8.2通过抛出指向包装器的指针并将直接存储在其位置(https://gcc.godbolt.org/z/nmibeo)成功地优化了这段代码。然而,按照注释更改代码行(即手动执行相同

  • 我是一个lisp初学者,我试图编写一个包,为trie定义一个类,并在其中读取拼字词典的全部内容。该结构充当一个节点,每个节点都有一个关联列表,该列表跟踪来自它的字母(导致其他子区)。 下面是我的类代码 这是我的添加单词函数 下面是打开我的文件(拼字字典)并读取每一行的函数 每当我试图加载整个字典时,都会出现堆栈溢出。拼字字典里有100k多个单词,但它在6000个时失败了……我的记忆使用情况出了问题

  • 今天我遇到了一些我不太理解的复制构造函数。 考虑下一个代码: 然后在将原点分配给复制时调用复制构造函数,这是有意义的。但是,如果我将复制的声明更改为 它不叫。即使当我使用< code>create()函数时,它也不会调用复制构造函数。但是,当将其更改为 它确实调用了复制构造函数。一些研究解释说,允许编译器优化复制构造函数,这听起来是件好事。直到复制构造函数是非默认的,因为那时它可能会,也可能不会做

  • 我正在尝试创建一些POD值的本地数组(例如,),其中包含编译时已知的固定 的程序集输出: 最新的GCC和MSVC编译器对栈的读写做了基本相同的事情。 如我们所见,读取并写入变量不会被优化掉。在循环开始之前,值被写入位置 调用中访问该位置的< code>array_wrapper.size变量(通过执行一些基于偏移量的奇怪操作)。 为什么? 这只是现代编译器实现中的一个小缺点吗(希望很快就会得到修复