14

为了方便和连贯,我倾向于使用常量表达式的数学函数(即log(x)/log(2),而不是log(x)/0.3...)。由于这些函数实际上并不是语言本身的一部分,因此它们也没有定义math.h(仅声明),常量函数会在编译时预先计算,还是会在运行时被浪费地计算?

4

6 回答 6

17

这取决于编译器和优化标志。正如@AndrewyT 指出的那样,GCC 能够通过属性指定哪些函数是常量和纯函数,在这种情况下,答案是肯定的,它将内联结果,您可以轻松检查:

$ cat constant_call_opt.c 
#include <math.h>

float foo() {
        return log(2);
}

$ gcc -c constant_call_opt.c -S

$ cat constant_call_opt.s 
        .file   "constant_call_opt.c"
        .text
.globl foo
        .type   foo, @function
foo:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $4, %esp
        movl    $0x3f317218, %eax
        movl    %eax, -4(%ebp)
        flds    -4(%ebp)
        leave
        ret
        .size   foo, .-foo
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

那里没有函数调用,只加载一个常量 ( 0x3f317218 == 0.69314718246459961 == log(2))

尽管我现在手头没有任何其他编译器可以尝试,但我认为您可以期望在所有主要的 C 编译器中都有相同的行为,因为这是一个微不足道的优化。

于 2010-01-12T16:37:20.993 回答
11

一些编译器会在编译时评估它们,但不能保证这种行为(这样做也可能导致问题)。你需要检查你的编译器,看看它做了什么。

请注意,在许多系统上,可log(2)M_LN2.<math.h>

于 2010-01-12T14:24:49.507 回答
3

它们通常会在运行时进行计算(有关如何内联它们,请参阅其他答案),尽管我不一定会使用“浪费”这个词,除非它们很多和/或代码被执行了很多次。

与其只是放入常量值,不如创建一个#defineconst变量来表达该值的含义(PILOG_2等)并使用它来代替。

例如:

#define LOG_2 0.30102999566

这不会进行任何计算,您可以将值指定为所需的精度或在给定您的环境(32 位 v 64 位)下可以管理的精度。

于 2010-01-12T14:04:14.760 回答
3

这取决于。如果编译器可以像在运行时一样进行数学运算,并且如果执行了链接时间优化,并且如果库以某种中间形式保存,那么是的,它可以完成。

大多数编译器并没有做到这一切。

于 2010-01-12T14:07:37.967 回答
3

在库函数的情况下,一些编译器可能将这些函数实现为内在函数,这意味着编译器对函数有足够的了解,可以在编译时用常量替换调用。是否会这样做取决于编译器。在实践中,我经常注意到一些编译器不愿意在编译时预先计算浮点表达式,即使它们不涉及任何函数调用。

在一般情况下,通常它们不会也无法在编译时计算,假设编译器根本不了解这些函数,无法在编译时计算它们。也许他们有一些突出的运行时副作用?

一些编译器可能具有非标准的依赖于编译器的扩展,允许用户向编译器提供有关函数的附加信息,以便编译器可以更好地优化函数调用,甚至确定是否可以用编译器替换给定的调用。时间预计算。例如,GCC 编译器支持这样的函数属性(GCC 特定的扩展)constpure. const如果参数在编译时是已知的,那么理论上可以用编译时预计算来替换具有该属性的函数调用。虽然我不能说 GCC 是否真的可以做到这一点。

在 C++(即使您的问题被标记为 C)语言中,一个计划中的新功能是一个constexpr声明说明符,它具有类似的目的并且应该具有您描述的效果。

于 2010-01-12T14:40:59.483 回答
0

这发生在运行时,因为函数的代码仅在链接步骤期间可用(发生在编译步骤之后)。

要优化该步骤,请使用在使用之前初始化的全局变量。

于 2010-01-12T14:04:37.327 回答