これらの属性により、コンパイラは、実装方法を知らなくても関数の呼び出しを省略しても安全かどうかを知ることができます。
pure 属性は基本的に、関数の結果が関数の引数とグローバルな状態にのみ依存することを示しています。さらに、関数自体はグローバル状態を変更しません。
純粋な関数を 2 回呼び出すと、同じ結果が返されることが保証されます。ただし、呼び出し間でグローバルに表示される状態を変更すると、保証は保持されなくなります。
const 属性は、グローバル状態が変更された場合でも、関数が同じ結果を返す必要があるという点でより強力です。したがって、より多くの場合に const 関数への冗長な呼び出しを最適化しても安全です。
状態が変化しないことを保証できる場合、グローバル状態の読み取りは問題になりません (グローバルを const としてマークしても常にそれが保証されるわけではないことに注意してください)。
例として、次のプログラムを考えてみましょう。
int foo(int) __attribute__((pure));
int bar(int) __attribute__((const));
void unknown();
int test1(int a)
{
int x = foo(a);
int y = foo(a);
return x + y;
}
int test2(int a)
{
int x = bar(a);
int y = bar(a);
return x + y;
}
int test3(int a)
{
int x = foo(a);
unknown();
int y = foo(a);
return x + y;
}
int test4(int a)
{
int x = bar(a);
unknown();
int y = bar(a);
return x + y;
}
gcc 4.8.1 でコンパイルしてアセンブリを分析すると、test1() と test2() の両方がそれぞれの関数を 1 回だけ呼び出してから、結果を 2 倍することがわかります。test3() は 3 回の呼び出しを行います - foo への 2 回の呼び出しと unknown への 1 回の呼び出し。test4() は、bar() の呼び出しに続いて unknown() の呼び出しを行い、bar() の結果を 2 倍して返します。
この動作は上記の説明と一致します。unknown() はグローバル状態を変更できるため、コンパイラは foo() への余分な呼び出しを省略できませんが、bar() への余分な呼び出しを省略できます。