【发布时间】:2015-04-08 22:33:37
【问题描述】:
GCC 具有 pure 和 const 属性,其中 const 实际上用于真正的纯函数(pure 用于idempotent functions which are also side-effect free)。
那么如何使用 const 属性声明和定义函数?
编辑:我对真正的纯函数感兴趣,即使用 const 属性声明的函数,而不是使用纯属性声明的函数。
【问题讨论】:
GCC 具有 pure 和 const 属性,其中 const 实际上用于真正的纯函数(pure 用于idempotent functions which are also side-effect free)。
那么如何使用 const 属性声明和定义函数?
编辑:我对真正的纯函数感兴趣,即使用 const 属性声明的函数,而不是使用纯属性声明的函数。
【问题讨论】:
例子:
// 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> ) ))。引用您链接到的文档:
关键字
__attribute__允许您在进行声明时指定特殊属性。此关键字后跟双括号内的属性说明。
您链接到的文档中有几个其他属性的示例,包括pure:
int square (int) __attribute__ ((pure));
因此,从语法上讲,使用const 所需的只是将pure 更改为const:
int square (int) __attribute__ ((const));
正如 cmets 中所指出的:如果您在定义中使用它,那么您需要将 __attribute__ ((const)) 放在不同的位置:
int square (int) __attribute__ ((const)) { ... } // doesn't work
int __attribute__ ((const)) square (int) { ... } // does work
但const 和pure 属性仅在应用于外部声明时才有用,所以这应该不是问题。如果定义是可见的,GCC 通常能够在没有你帮助的情况下确定函数是否可以被视为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 也不关心我们是否会改变函数内部的全局状态并在每次调用时返回不同的值,因为它会导致全局状态发生变化。
#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
【讨论】:
printf 可能会修改全局状态,而pure 函数可能依赖于全局状态,因此也会强制进行另一个调用。对于__attribute__((const)) 和-O,但不是-O2,我确实看到40 打印了三次。
从 C++11 开始,可以使用 attribute specifier sequence 来指定此类属性。例如:
[[ gnu::const ]]
int square (int x)
{
return x * x;
}
此外,从 C++17 开始,编译器未知的所有属性都会被忽略而不会导致错误。所以上面的代码可以在不同的编译器和平台之间移植。
【讨论】: