フック関数など、特に必要がなければ、実体は何も定義しないまま、関数のシンボルを0番地に配置しておき、必要になった時点で実際の関数で置き換えるといった技法がよく使われます。

具体的には、GNUのリンカスクリプトでPROVIDEによってウィークなシンボルを定義したり、GCCのweak alias属性を付けたり、インラインアセンブラを使ったり、シンボルを0に配置するアセンブリ言語のソースとCの関数定義を使い分けたりといった具合です。

このとき、フック関数を呼び出す箇所では、関数が存在するかどうかを調べるために、0(空ポインタ)との比較を行うことになります。

ところが、標準Cでは、いかなる関数へのポインタも空ポインタと等しくならないことが、言語仕様上保証されています。結果として、hookのアドレスを実際に調べるまでもなく、hook != 0は常に真であると判断しても、本来であれば問題がないわけです。

結果として、最適化オプションを付けてコンパイルすると、hook != 0の部分は常に真として扱われ、実際にはhookが0番地であり、関数定義が存在しないにも関わらず、hook関数を呼び出してしまうといった事態を招きます。

元々が、通常のCの使い方から逸脱した技法で、処理系に依存した方法を使った結果として起こる問題なのですが、こんな場合でも、極力移植性のある記述を行わなければならないことがあります。

この問題を回避する方法はいくつか検討されたのですが、最終的に落ち着いた方法だけを紹介することにします。

ここで、funcは関数へのポインタを格納するための変数なのですが、volatileが付いているところがポイントです。volatileが付いていると、コンパイラが知らない方法でfuncの値が変化するかもしれないため、func != 0が常に真であるとみなすことができなくなります。結果として、func != 0の評価は期待通り行われることになります。