接受指针参数的GCC纯/常量函数

15

请问一下,如果一个函数有一个指针参数,它是否可以被标记为pureconst,为什么?

根据GCC文档

纯函数的一些常见例子是strlen或memcmp。

纯函数的整个意义在于,对于相同的参数,它只需要被调用一次,也就是说如果编译器认为合适,结果就可以被缓存。然而,对于memcmp,这是如何工作的呢?

例如:

char *x = calloc(1, 8);
char *y = calloc(1, 8);

if (memcmp(x, y, 8) > 0)
    printf("x > y\n");

x[1] = 'a';
if (memcmp(x, y, 8) > 0)
    printf("x > y\n");
第二次调用 memcmp 的参数与第一次相同(指针指向相同的地址),如果 memcmp 是纯函数,编译器如何知道不使用第一次调用的结果?
我想将一个数组传递给一个纯函数,并仅基于该数组计算结果。有人可以向我保证这是可以的,并且当数组中的值发生变化但地址不变时,我的函数会被正确调用。
2个回答

4
如果我理解文档正确的话,一个“纯函数”可以依赖于内存的值,编译器知道内存何时发生了变化。此外,“纯函数”不能改变程序的状态,例如全局变量,它只产生返回值。
在你的示例代码中,"memcmp"可以是一个"纯函数"。编译器看到在调用"memcmp"之间内存已经被更改,无法重用第一次调用的结果进行第二次调用。
另一方面,"memcmp"不能被声明为"const"函数,因为它依赖于内存中的数据。如果它是"const",编译器可以应用更激进的优化。
因此,将您想要实现的函数声明为"pure"(但不是"const")似乎是安全的。

3
关于pure,我们可以从文章Implications of pure and constant functions中看到,pure意味着函数没有副作用,仅依赖于参数。
因此,如果编译器能够确定参数相同且在后续调用之间内存未更改,它可以消除对纯函数的后续调用,因为它知道纯函数没有副作用。
这意味着编译器必须进行分析,以确定是否在决定消除相同参数的纯函数的后续调用之前修改了纯函数的参数。
文章中的一个示例如下:
int someimpurefunction(int a);
int somepurefunction(int a)
  __attribute__((pure));

int testfunction(int a, int b, int c, int d) {

  int res1 = someimpurefunction(a) ? someimpurefunction(a) : b;
  int res2 = somepurefunction(a) ? somepurefunction(a) : c;
  int res3 = a+b ? a+b : d;

  return res1+res2+res3;
}

它显示了优化生成的汇编代码,这表明somepurefunction仅被调用一次,然后说:

正如您所看到的,纯函数只被调用一次,因为三元运算符中的两个引用是等价的,而另一个则被调用了两次。这是因为在两次纯函数调用之间,编译器不知道全局内存有任何变化(函数本身不能更改它)-请注意,即使通过-pthread标志显式请求多线程,编译器也永远不会考虑多线程,而非纯函数允许更改全局内存或使用I/O操作。

这种逻辑也适用于指针,因此如果编译器可以证明指向指针的内存没有被修改,则可以消除对纯函数的调用,在您的情况下,当编译器看到以下内容时:

x[1] = 'a';

由于指向 x 的内存已经更改,因此无法消除对 memcmp 的第二次调用。


1
谢谢提供文章链接,很有趣。但我的问题特别涉及到纯函数的指针参数。 - jsj
如果在编译时不知道8和1会发生什么情况?例如,它们是变量? - Nghia Bui
如果编译器无法证明内存未被改变,它必须假设内存可能已经被改变。因此,它不会执行(不安全的)优化。 - rici
好的,现在很清楚了。这个答案应该被接受 :) - Nghia Bui
如果您在结尾添加一个小节,说明相同的逻辑也适用于指针,我将接受这个答案(我只是想确保通过谷歌而非阅读这些评论到达此处的人们感到他们的问题已得到回答)。 - jsj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接