7

私は O'Reilly の Practical C Programming の本を読んでおり、C プログラミング言語に関する K&R の本を読んでいますが、共用体の背後にある概念を理解するのに本当に苦労しています。

それらはそれらを構成する最大のデータ型のサイズを取ります...そして最後に割り当てられたものは残りを上書きします...しかし、必要に応じてメモリを使用/解放しないのはなぜですか?

この本は、同じサイズのフラグを設定する必要がある通信で使用されると述べています。そしてグーグルのウェブサイトでは、それは奇数サイズのメモリチャンクを排除できることを...しかし、それは最新の非埋め込みメモリ空間で何か役に立ちますか?

それとCPUレジスタでできる巧妙なことはありますか? それは単にプログラミングの初期の時代からの持ち越しですか?それとも、悪名高い goto のように、保持する価値のある強力な用途がまだ (おそらく狭いメモリ空間で) あるのでしょうか?

4

5 に答える 5

5

さて、あなたはほとんどあなたの質問に答えました: メモリ. 昔はメモリが少なかったので、数キロバイトを節約するだけでも役に立ちました。

しかし、今日でもユニオンが役立つシナリオがあります。variantたとえば、ある種のデータ型を実装したい場合。これを行う最善の方法は、ユニオンを使用することです。

これは大したことではないように思えますが、4 文字の文字列 (ID など) または 4 バイトの数値 (ハッシュまたは単なる数値) を格納する変数を使用したいと仮定しましょう。

クラシックを使用する場合struct、これは 8 バイトの長さになります (少なくとも、運が悪い場合は、バイトもいっぱいになります)。を使用するunionと、わずか4バイトです。つまり、50% のメモリを節約できます。これは 1 つのインスタンスではそれほど多くはありませんが、これらが 100 万個あると想像してみてください。

ユニオンをキャストまたはサブクラス化することで同様のことを実現できますが、それでもこれを行う最も簡単な方法です。

于 2013-03-28T00:01:34.913 に答える
1

共用体の 1 つの使用法は、2 つの変数が同じスペースを占有し、構造体の 2 番目の変数がそれを読み取るデータ型を決定することです。

たとえば、ブール値の「isDouble」と、double と long の両方を持つユニオン「doubleOrLong」を使用できます。isDouble == true の場合、union を double として解釈し、それ以外の場合は long として解釈します。

共用体のもう 1 つの用途は、異なる表現でデータ型にアクセスすることです。たとえば、double がメモリ内でどのように配置されるかを知っている場合、double を共用体に入れ、long のような別のデータ型としてアクセスし、そのビット、仮数、符号、指数などに直接アクセスできます。 、それを直接操作します。

メモリは非常に安価であるため、最近ではこれは必要ありませんが、組み込みシステムでは用途があります。

于 2013-03-27T23:57:53.933 に答える
0

Doom 3/idTech 4 Fast Inverse Square Rootの実装で使用されているのを見たことがある場所の 1 つです。

このアルゴリズムに慣れていない人にとっては、基本的に浮動小数点数を整数として扱う必要があります。コードの古い Quake (およびそれ以前) のバージョンは、次のようにしてこれを行います。

float y = 2.0f;

// treat the bits of y as an integer
long i  = * ( long * ) &y;

// do some stuff with i

// treat the bits of i as a float
y = * ( float * ) &i;

GitHub の元のソース

このコードは、浮動小数点数 のアドレスを取得yし、それを long (つまり、Quake 時代の 32 ビット整数) へのポインターにキャストし、それを に逆参照しiます。次に、信じられないほど奇妙なビットいじりを行い、その逆を行います。

この方法には、2 つの欠点があります。1 つは、入り組んだアドレス オブ キャスト、逆参照プロセスにより、 の値がレジスタ1yからではなく、メモリから読み取られるように強制されることです。ただし、Quake 時代のコンピューターでは、浮動小数点レジスタと整数レジスタは完全に分離されていたため、この制限に対処するには、メモリにプッシュして戻す必要がありました。

2 つ目は、少なくとも C++ では、この関数のようにブードゥーに相当することを行う場合でも、そのようなキャストを行うことは非常に嫌われていることです。もっと説得力のある議論があると確信していますが、それらが何であるかはわかりません:)

そのため、Doom 3 では、id は新しい実装に次のビットを含めました (これは別のビット調整セットを使用しますが、同様のアイデアを使用します)。

union _flint {
        dword                   i;
        float                   f;
};

...
union _flint seed;
seed.i = /* look up some tables to get this */;
double r = seed.f; // <- access the bits of seed.i as a floating point number

GitHub の元のソース

理論的には、SSE2 マシンでは、これは単一のレジスタを介してアクセスできます。コンパイラがこれを行うかどうかは実際にはわかりません。私の意見では、以前の Quake バージョンのキャスティング ゲームよりも、コードはまだいくらかクリーンです。


1 - 「十分に高度なコンパイラ」引数を無視する

于 2013-03-28T01:26:52.603 に答える
0

Windows API は共用体を非常によく利用します。LARGE_INTEGERは、そのような使用法の例です。基本的に、コンパイラが 64 ビット整数をサポートしている場合は、QuadPartメンバーを使用します。それ以外の場合は、低 DWORD と高 DWORD を手動で設定します。

于 2013-03-28T00:02:39.000 に答える
0

C 言語が作成されたのは 1972 年で、当時はメモリが重要な問題でした。

現代の非組み込み空間では、そもそも C をプログラミング言語として使用したくないという議論をすることができます。実装用の言語として C を選択した場合は、C の利点を利用しようとしています。C は効率的で、金属に近く、タイトで高速なバイナリが得られます。

そのため、C の使用を選択した場合でも、メモリ空間の効率性など、C の利点を活用したいと考えるでしょう。これに対して、連合は非常にうまく機能します。利用可能な最小のメモリフットプリントを適用しながら、ある程度のタイプセーフを実現できます。

于 2013-03-28T00:03:40.613 に答える