13

このホワイト ペーパーでは、ゼロ除算をトリガーできるコードの例を次に示します。

if (arg2 == 0)
    ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
                    errmsg("division by zero")));
/* No overflow is possible */
PG_RETURN_INT32((int32) arg1 / arg2);

ereportこれは、返される場合と返されない場合があるbool-returning 関数への呼び出しに展開されるマクロと、その戻り値の条件付き ( a を使用)、別の関数への呼び出しです。この場合、レベルが無条件に他の場所を引き起こすと私は信じています。errstart?:ereportERRORlongjmp()

したがって、上記のコードの単純な解釈では、arg2がゼロでない場合は除算が行われ、結果が返さarg2れますが、 がゼロの場合はエラーが報告され、除算は行われません。ただし、リンクされた論文は、C コンパイラがゼロ チェックの前に除算を正当に持ち上げ、ゼロ チェックが決してトリガーされないと推測する可能性があると主張しています。私には正しくないように思われる彼らの唯一の推論は、

[T]プログラマーは、ereport(ERROR, : : :) の呼び出しが返されないことをコンパイラーに通知できませんでした。これは、除算が常に実行されることを意味します。

John Regehr には、より簡単な例があります。

void bar (void);
int a;
void foo3 (unsigned y, unsigned z)
{
  bar();
  a = y%z;
}

このブログ投稿によると、clang は への呼び出しの上にモジュロ演算を巻き上げ、barそれを証明するアセンブリ コードを示しています。

これらのスニペットに適用される C についての私の理解は、

  1. 戻らない、または戻らない可能性がある関数は、標準 C では整形式であり、そのような関数の宣言には、特定の属性、付属品、ホイッスルは必要ありません。

  2. 戻らない、または戻らない可能性がある関数の呼び出しのセマンティクスは、特に C99 の 6.5.2.2「関数呼び出し」によって明確に定義されています。

  3. ereport呼び出しは完全な式であるため、. にシーケンス ポイントがあります;。同様に、barJohn Regehr のコードの呼び出しは完全な式であるため、. にシーケンス ポイントがあり;ます。

  4. したがって、ereport呼び出しまたはbar呼び出しと除算またはモジュロの間にシーケンス ポイントがあります。

  5. C コンパイラは、独自に未定義の動作を誘発しないプログラムに未定義の動作を導入することはできません。

これらの 5 つのポイントは、上記のゼロ除算テストが正しく記述されており、呼び出しの上にモジュロを持ち上げることが正しくないと結論付けるのに十分であるように思われますbar。2 人の編集者と多数の専門家が意見を異にしています。私の推論のどこが間違っていますか?

4

2 に答える 2