将函数声明为 pure 或 const 对 GCC 的影响(如果不是)

Effects of declaring a function as pure or const to GCC, when it isn't

GCC 可以使用标志 -Wsuggest-attribute=pure-Wsuggest-attribute=const.

建议属性 pure 和属性 const 的函数

GCC documentation 说:

Many functions have no effects except the return value and their return value depends only on the parameters and/or global variables. Such a function can be subject to common subexpression elimination and loop optimization just as an arithmetic operator would be. These functions should be declared with the attribute pure.

但是,如果将 __attribute__((__pure__)) 附加到与上述描述不符且有副作用的函数,会发生什么情况?仅仅是函数调用次数少于您希望的次数的可能性,还是可能产生未定义的行为或其他类型的严重问题?

__attribute__((__const__)) 类似,后者再次更严格 - 文档指出:

Basically this is just slightly more strict class than the pure attribute below, since function is not allowed to read global memory.

但是,如果将 __attribute__((__const__)) 附加到访问全局内存的函数,实际上 会发生什么?

我更喜欢在 GCC / G++ 范围内解释实际可能情况的技术答案,而不是通常的“nasal demons”每当提到未定义的行为时都会出现的挥手。

But what can happen if you attach __attribute__((__pure__)) to a function that doesn't match the above description, and does have side effects?

没错。这是一个简短的例子:

extern __attribute__((pure)) int mypure(const char *p);

int call_pure() {
  int x = mypure("Hello");
  int y = mypure("Hello");
  return x + y;
}

我的 GCC (4.8.4) 版本足够聪明,可以删除对 mypure 的第二次调用(结果是 2*mypure())。现在想象如果 mypureprintf - 打印字符串 "Hello" 的副作用将会丢失。

请注意,如果我将 call_pure 替换为

char s[];

int call_pure() {
  int x = mypure("Hello");
  s[0] = 1;
  int y = mypure("Hello");
  return x + y;
}

两个调用都将发出(因为分配给 s[0] 可能会更改 mypure 的输出值)。

Is it simply the possibility that the function will be called fewer times than you would want it to be, or is it possible to create undefined behaviour or other kinds of serious problems?

嗯,它可以间接导致UB。例如。这里

extern __attribute__((pure)) int get_index();

char a[];
int i;
void foo() {
  i = get_index();  // Returns -1
  a[get_index()];  // Returns 0
}

编译器很可能会放弃对 get_index() 的第二次调用并使用第一个 returned 值 -1,这将导致缓冲区溢出(好吧,技术上是下溢)。

But what can actually happen if you attach __attribute__((__const__)) to a function that does access global memory?

让我们再次以上面的例子为例

int call_pure() {
  int x = mypure("Hello");
  s[0] = 1;
  int y = mypure("Hello");
  return x + y;
}

如果 mypure 被注释为 __attribute__((const)),编译器将再次放弃第二次调用并将 return 优化为 2*mypure(...)。如果 mypure 实际上读取 s,这将导致产生错误的结果。

编辑

我知道你要求避免 hand-waving 但这里有一些通用的解释。默认情况下,函数调用会阻止编译器内部的许多优化,因为它必须被视为可能具有任意副作用(修改任何全局变量等)的黑盒。用 const 或 pure 注释函数可以让编译器更像表达式一样对待它,这样可以进行更积极的优化。

例子实在是多得无法举出。我在上面给出的是公共子表达式消除,但我们也可以轻松地证明循环不变量、死代码消除、别名分析等的好处。