0

このようにキャストすることは合法ですか?

void probability(void **value)
{
    double v = 0.1234;
    *value = (void *) *(uint64_t *) &v;
}

これは悪いことですが、ターゲット マシンでsizeof(double)= sizeof(void *)=が実行されることは 100% 確信しています。sizeof(uint64_t)

4

3 に答える 3

6

コードが厳密なエイリアシング規則に違反しているため、これは未定義の動作です。

コンパイラは、ほとんどの無関係な型へのポインターが同じメモリを指していないと想定することが許可されています。uint64_t *「実際には」double であるメモリを指す (キャストの結果) を作成し、そのポインターから読み取ると、double と関係のある値が得られることを期待します。

厳密なエイリアシング ルールの目的は、コンパイラがこのコードを壊すさまざまな最適化を行えるようにすることです。最も可能性が高いvのは、有効な名前またはポインター、無効なエイリアスのみ。

この特定のコードを確認したことはありませんが、実際には GCC は高度な最適化で厳密なエイリアシングに依存しており、この種のコードを壊します。

厳密なエイリアシングの問題を修正する方法は次のmemcpyとおりです。

assert(sizeof(*value) == sizeof(v));
memcpy(value, &v, sizeof(*value));

これが完了した後、または厳密なエイリアシングに依存していないコンパイラ、またはその依存を回避できるコンパイラを使用している場合 ( --no-strict-aliasing)、まだ問題があります。標準では、アドレスと同じサイズのすべての数値が実際に で表現できることを保証していませんvoid*。たとえば、実装が (事実上) ポインターにパディング ビットを持ち、パディング ビットに間違った値でポインター値を作成しようとするとクラッシュすることは合法です。実際には、「通常」と呼ばれるハードウェアでは発生しませんが、それでも標準ではコードを許可していません。

于 2012-04-23T13:28:01.223 に答える
3

厳密に言えば、いいえ。一部

*(uint64_t*)&v;

未定義の動作です。uint64_ttoのキャストvoid*は有効ですが、無意味な場合があります。そのようなキャストは実装定義です (6.3.2.3)。

于 2012-04-23T13:23:05.303 に答える
1

未定義の動作。どんなに確信があっても。

于 2012-04-23T13:23:01.123 に答える