21

たとえば、ポインターとbool. 簡単にするために、例ではポインターが使用されますが、ポインターの型は、 1 より大きいintものを指している限り関係ありません。size()

クラスをデータ メンバーで定義すると、クラス{ bool , int *}のサイズがポインターのサイズの 2 倍になり、多くの無駄なスペースが発生します。

charポインターが(または の他のデータ)を指していない場合、size(1)おそらく下位ビットは常に 0 になります。クラスは{int *}便宜上 または で定義できます。union { int *, uintptr_t }

これboolは、論理値に従ってポインターの下位ビットを設定/クリアし、ポインターboolを使用する必要があるときにビットをクリアすることによって実装されます。

定義された方法:

struct myData
{
 int * ptr;
 bool flag;
};
myData x;

// initialize
x.ptr = new int;
x.flag = false;

// set flag true
x.flag = true;

// set flag false
x.flag = false;

// use ptr
*(x.ptr)=7;

// change ptr
x = y;                // y is another int *

そして提案された方法:

union tiny
{
 int * ptr;
 uintptr_t flag;
};
tiny x;

// initialize
x.ptr = new int;

// set flag true
x.flag |= 1;

// set flag false
x.flag &= ~1;

// use ptr
tiny clean=x;      // note that clean will likely be optimized out
clean.flag &= ~1;  // back to original value as assigned to ptr
*(clean.ptr)=7;

// change ptr
bool flag=x.flag;
x.ptr = y;             // y is another int *
x.flag |= flag;

これは未定義の動作のようですが、これはどの程度移植性がありますか?

4

4 に答える 4

13

ポインターとして使用する前にポインターの下位ビットを復元する限り、システム、C++ 実装、およびコードが特定の前提条件を満たしている限り、"合理的に" 移植可能である可能性があります。

必ずしも仮定の完全なリストを提供することはできませんが、頭の中で次のように述べています。

  • サイズが 1 バイトのものを指していないことを前提としています。charunsigned charsigned charint8_t、およびは除外されuint8_tます。CHAR_BIT == 8(そして、それは; たとえば、16 ビットまたは 32 ビットのバイトを持つエキゾチックなシステムでは、他のタイプが除外される可能性があると仮定しています。)
  • これは、サイズが 2 バイト以上のオブジェクトが常に偶数アドレスに配置されていることを前提としています。x86 ではこれは必要ないことに注意してください。int奇数アドレスで4 バイトにアクセスできますが、少し遅くなります。ただし、コンパイラは通常、オブジェクトが偶数アドレスに格納されるように調整します。他のアーキテクチャでは、要件が異なる場合があります。
  • 偶数アドレスへのポインターの下位ビットが 0 に設定されていると想定しています。

その最後の仮定に対して、私は実際に具体的な反例を持っています。Cray ベクトル システム (J90、T90、および SV1 は私が使用したものです) では、マシン アドレスは 64 ビット ワードを指しますが、Unicos の C コンパイラはCHAR_BIT == 8. バイト ポインターはソフトウェアで実装され、1 ワード内の 3 ビット バイト オフセットは、使用されない64 ビット ポインターの上位 3 ビットに格納されます。そのため、8 バイトにアラインされたオブジェクトへのポインターは、その下位ビットを簡単に 1 に設定できます。

型タグを格納するためにポインターの下位 2 ビットを使用するLisp 実装 ( example ) がありました。これにより、移植中に深刻な問題が発生したことを漠然と覚えています。

結論:ほとんどのシステムでは、おそらく問題なく実行できます。将来のアーキテクチャはほとんど予測不可能であり、次のビッグ ニュー シングであなたのスキームが破綻することは容易に想像できます。

考慮すべき事項:

クラスのビットベクトルにブール値を格納できますか? (ポインタとビット ベクトル内の対応するビットとの間の関連付けを維持することは、演習として残されています)。

下位ビットが 1 に設定されたポインターが検出された場合にエラー メッセージを表示して失敗するすべてのポインター操作にコードを追加することを検討して#ifdefください。製品バージョンでチェック コードを削除するために使用します。一部のプラットフォームで問題が発生し始めた場合は、チェックを有効にしてコードのバージョンをビルドし、何が起こるかを確認してください。

アプリケーションが大きくなるにつれて (アプリケーションが縮小することはめったにありません)、boolポインタと一緒に a 以外のものを格納する必要があると思います。その場合、余分なスペースをすでに使用しているため、スペースの問題はなくなります。

于 2013-11-15T02:05:43.833 に答える
4

「理論」では、私の知る限り、未定義の動作です。

「現実」では、日常の x86/x64 マシンで動作し、おそらく ARM でも動作するでしょうか?
それ以上の発言は本当にできない。

于 2013-11-15T00:53:57.200 に答える
3

これは非常に移植性が高く、さらに、assert生のポインターを受け入れると、アライメント要件を満たしていることを確認できます。これにより、どういうわけかあなたを台無しにする計り知れない将来のコンパイラーを防ぐことができます。

そうしない唯一の理由は、そのような「ハッキー」なものに関連する読みやすさのコストと一般的なメンテナンスです。明確な利益が得られない限り、私はそれを避けたいと思います. しかし、時にはそれだけの価値があります。

于 2015-09-15T09:30:36.783 に答える
0

これらのルールに準拠しており、非常に移植性が高いはずです。

于 2013-11-15T00:54:52.080 に答える