如何在 gcc 中声明和定义一个纯函数?

How to declare and define a pure function in gcc?

GCC 有 pure 和 const 属性,其中 const 实际上用于真正的纯函数(pure 用于 idempotent functions which are also side-effect free)。

那么如何使用 const 属性声明和定义函数?

编辑:我对真正的纯函数感兴趣,那些用 const 属性声明的函数,而不是那些用 pure 属性声明的函数。

根据这个 this article,语法与 @hvd 所说的匹配:

int square (int) __attribute__ ((pure));

但是,当我编译以下示例时,gcc 似乎没有强制执行不检查全局状态的 属性。

#include <stdio.h>

int square (int) __attribute__ ((pure));

int outerX = 7;
int square(int x) {
   return outerX * x; 
}

int main(){
    printf("%d\n", square(5));
    return 0;
}

以下未打印任何错误,代码运行并生成 35.

gcc -Wall -Werror -pedantic -O3 Pure.c


gcc --version
gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1

更奇怪的是,gcc 也不关心我们是否在函数内部改变了全局状态,并且 return 每次调用都会因为它在全局状态中引起的变化而产生不同的值。

#include <stdio.h>

int square (int) __attribute__ ((pure));

int outerX = 7;
int square(int x) {
   outerX++;
   return outerX * x; 
}

int main(){
    printf("%d\n", square(5));
    printf("%d\n", square(5));
    printf("%d\n", square(5));
    return 0;
}

输出:

40
45
50

示例:

// Declaration:
int square (int x) __attribute__ ((const));
// Definition:
int __attribute__ ((const)) square (int x)
{ 
    return x*x; 
}

所有属性的语法几乎相同:__attribute__ (( <attribute-name> ))__attribute__ (( <attribute-name> ( <attribute-options> ) ))。从文档中引用你 link 到:

The keyword __attribute__ allows you to specify special attributes when making a declaration. This keyword is followed by an attribute specification inside double parentheses.

文档中有您 link 几个其他属性的示例,包括 pure:

int square (int) __attribute__ ((pure));

因此,在语法方面,要使用 const,您只需将 pure 更改为 const:

int square (int) __attribute__ ((const));

正如评论中指出的那样:如果您在定义中使用它,那么您需要将 __attribute__ ((const)) 放在不同的位置:

int square (int) __attribute__ ((const)) { ... } // doesn't work
int __attribute__ ((const)) square (int) { ... } // does work

但是 constpure 属性几乎只有在应用于外部声明时才有用,所以这应该不是问题。如果定义可见,GCC 通常能够确定该函数是否可以在没有您帮助的情况下被视为 const/pure

从 C++11 开始,可以使用 attribute specifier sequence 来指定此类属性。例如:

[[ gnu::const ]]
int square (int x)
{ 
    return x * x; 
}

此外,从 C++17 开始,编译器未知的所有属性都将被忽略而不会导致错误。所以上面的代码可以在不同的编译器和平台之间移植。