为什么在 GCC 中错误使用 __attribute__((pure)) 没有给出警告?

Why is no warning given for the wrong use of __attribute__((pure)) in GCC?

我正在尝试理解纯函数,并且一直在阅读有关该主题的 Wikipedia article。我写的最小示例程序如下:

#include <stdio.h>

static int a = 1;

static __attribute__((pure)) int pure_function(int x, int y)
{
        return x + y;
}

static __attribute__((pure)) int impure_function(int x, int y)
{
        a++;
        return x + y;
}

int main(void)
{
        printf("pure_function(0, 0) = %d\n", pure_function(0, 0));
        printf("impure_function(0, 0) = %d\n", impure_function(0, 0));

        return 0;
}

我用 gcc -O2 -Wall -Wextra 编译了这个程序,期望用 __attribute__((pure)) 修饰 impure_function() 时应该发出错误,或者至少是警告。但是,我没有收到任何警告或错误,程序也 运行 没有问题。

__attribute__((pure))标记impure_function()是不是不正确?如果是这样,为什么即使使用 -Wextra-Wall 标志,它编译时也没有任何错误或警告?

提前致谢!

纯函数是 optimizing compiler. Probably, gcc don't care about pure functions when you pass just -O0 to it (the default optimizations). So if f is pure (and defined outside of your translation unit 的提示,例如在某些外部库中),GCC 编译器 可能 y = f(x) + f(x); 优化为

{ 
  int tmp = f(x); /// tmp is a fresh variable, not appearing elsewhere
  y = tmp + tmp;
}

但是如果 f 不是 纯(这是通常的情况:想想 f 调用 printfmalloc), 这样的优化是被禁止的。

标准数学函数,如 sin or sqrt are pure (except for IEEE rounding mode craziness, see http://floating-point-gui.de/ and Fluctuat 等),它们足够复杂,可以进行计算,使此类优化值得。

您可以使用 gcc -O2 -Wall -fdump-tree-all 编译您的代码以猜测编译器内部发生了什么。您可以添加 -fverbose-asm -S 标志以获取生成的 *.s 汇编程序文件。

您还可以阅读 Bismon draft report(尤其是其第 1.4 节)。它可能会给出一些与您的问题相关的直觉。

在您的具体情况下,我猜测 gcc 是您的电话 inlining;然后纯度就不那么重要了。

如果您有时间,可以考虑编写自己的幻灯片GCC plugin to make such a warning. You'll spend months in writing it! These old,即使详细信息已过时,也可能对您有用。

理论层面,要注意Rice's theorem。其结果是纯函数的完美优化可能是不可能的。

注意位于孟买的 GCC Resource Center

这样做是不正确的,您有责任正确使用该属性。

看这个例子:

static __attribute__((pure)) int impure_function(int x, int y)
{
        extern int a;
        a++;
        return x + y;
}

void caller()
{
    impure_function(1, 1);
}

GCC(带有 -O1)为函数 caller 生成的代码是:

caller():
        ret

如您所见,impure_function 调用已完全删除,因为编译器将其视为 "pure"。

如果 GCC 看到它的定义,它可以在内部自动将函数标记为 "pure":

static __attribute__((noinline)) int pure_function(int x, int y)
{
        return x + y;
}

void caller()
{
    pure_function(1, 1);
}

生成的代码:

caller():
        ret

因此,在编译器可见的函数上使用此属性没有意义。它应该在定义不可用时使用,例如,当函数在另一个 DLL 中定义时。这意味着当它在适当的地方使用时,编译器将无法执行完整性检查。因此实现警告不是很有用(尽管并非毫无意义)。

我认为没有什么可以阻止 GCC 开发人员实施此类警告,除了必须花费的时间。