1

昨日、次の関数を見ました。

static void swap (int *p, int *q) {
    *p -= *q;
    *q += *p;
    *p = *q - *p;
}

この機能が動作しないのはどのような場合ですか? 潜在的にオーバーフローの問題があり、非効率的である可能性があると思います。他に何かありますか?

4

2 に答える 2

4

オーバーフローの問題は「たぶん」ではなく、確かにあります。大きな負の値を大きな正の値と交換すると、オーバーフローが発生します。これは、標準では未定義の動作です。

しかし、最も重要なことは、読みやすさの理由から、通常の交換(一時変数を介して)の代わりにこれ(またはxorトリック)を使用する意味がまったくないことです。

于 2012-09-15T12:49:02.863 に答える
1

実際、上記の答えは間違っています。p に対して 0xFFFFFFFE を取得するのは、q を 0x7FFFFFFFE で初期化したためです。これは適合せず、(未定義の動作を除いて) 実際には 0xFFFFFFFE に切り捨てられます。

したがって、その回答のコードは実際には p = 0xFFFFFFFF と q = 0xFFFFFFFE で始まり、それらを正常に交換します。

(編集:それは間違っていました、それは今修正されました)

実際、ルーチンは完全に機能します (最終的な未定義の動作が存在するかどうかわからないことを除いて、標準の詳細については少し曖昧です)。

最終的な未定義の動作を無視して、基礎となる算術が一般的な 2 の補数モジュロ算術である場合、ルーチンは機能し、オーバーフロー/アンダーフローの問題はありません (これはモジュロ算術です)。

唯一の本当の問題はエイリアシングにあります。2 つのポインターが同一の場合、ポインター先の整数は 0 になります。

したがって、このルーチンの前提条件は、2 つのポインターが有効であり、エイリアス化されていないことです。プログラムで必要と思われる場合は、null ポインターと同一のポインターのチェックを追加できます。

なぜ一時変数スワップの代わりに xor トリックの代わりにこのメカニズムを使用するのかという問題は未解決です。しかし、OPの質問とは何の関係もありません:ルーチンは機能し、プロセッサで最も一般的な2の補数モジュロ演算によって処理されるため、オーバーフローは問題ではありません(編集:オーバーフローが未定義の動作であるためにのみ発生します標準ですが、2 の補数モジュロ演算を使用するすべてのプロセッサで動作します)。効率に関しては、すべてオプティマイザとプロセッサに依存します。

于 2012-09-15T14:08:10.877 に答える