5

私は最近、何かがコンパイルされることを発見しました(それが合法かどうかはわかりませんが)。私のそのようなものの必要性はこれから来ます:私のプロジェクトは選択されたアーチのマシンコードを出力します(これはプログラムを実行しているものと同じアーチであるかもしれないし、そうでないかもしれません)。したがって、今すぐ最大64ビットアーキテクチャをサポートしたいと思います(既存の32ビットおよび16ビットアーチもサポートします)。現在の解決策は、new_stateの「ベース」をuint64_tにし、必要に応じて手動で16ビットと32ビットにキャストすることです。 。ただし、関数パラメーターで共用体をコンパイルできることを発見しました。したがって、これがコンパイルされる関数は次のとおりです。

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz,
             union{
    uint16_t b16;
    uint32_t b32;
    uint64_t b64;
}base ,int self_running);

この種のことは、他のコンパイラによってサポートされていますが、まったく「合法」ですか?また、ユニオンを作成してこのユニオンをnew_stateに渡さずに、この関数を呼び出す方法を理解することはできません。

4

5 に答える 5

7

要約すると、はい、C++ では違法ですが、C では有効です。後者には、違いを説明するこのメモが含まれています

変更: C++ では、戻り値またはパラメーターの型で型を定義できない場合があります。C では、これらの型定義が許可されています。

例:

void f( struct S { int a; } arg ) {} // valid C, invalid C++
enum E { A, B, C } f() {} // valid C, invalid C++
  • 理論的根拠:異なるコンパイル単位で型を比較す​​る場合、C が構造的な等価性に依存しているのに対し、C++ は名前の等価性に依存しています。パラメーターの型について: パラメーター リストで定義された型は関数のスコープ内にあるため、C++ での唯一の正当な呼び出しは関数自体からのものです。
  • 元の機能への影響:意味的に明確に定義された機能の削除。
  • 変換の難しさ:意味変換。型定義は、ファイル スコープまたはヘッダー ファイルに移動する必要があります。
  • 広く使用されている方法:めったに使用されません。このスタイルの型定義は、不適切なコーディング スタイルと見なされます。

C における構造上の同等性は、「型の互換性」の概念によって行われます。これにより、C では多くの型を同一であるかのように扱うことができます。たとえそれらが理論的には異なっていても、2 つの異なる翻訳単位で宣言されているためです。C++ では、この概念は存在しません。型にはリンケージがあり、同じエンティティに一致するためです (つまり、メンバー関数が互いにリンクできるようにするため)。

上記の説明は、型の互換性を判断する際に構造体のタグ名を考慮しなかった C89 に基づいていることに注意してください。C89 ドラフトでは、関連するテキストは次のようになります。

さらに、別々の翻訳単位で宣言された 2 つの構造体、共用体、または列挙型は、同じ数のメンバー、同じメンバー名、および互換性のあるメンバー型を持っている場合、互換性があります。2 つの構造体の場合、メンバーは同じ順序でなければなりません。

C99 では、型チェックがより厳密になります。1 つの構造体にタグ名がある場合、もう 1 つの構造体宣言には同じタグ名が必要です。したがって、名前のない共用体型の場合、互換性のある型を持つ別の TU で関数を宣言するには、有効な C99 コード (未定義の動作なし) が必要な場合は、名前のない共用体が再び必要になります。「だます」ことはできません。ある TU で名前付き共用体を使用し、別の TU で無名共用体を使用します。ただし、この「トリック」はC89にも有効であるように見えます。C99 TC3 6.2.7/1:

さらに、別々の翻訳単位で宣言された 2 つの構造体、共用体、または列挙型は、それらのタグとメンバーが次の要件を満たしている場合に互換性があります。両方とも完全な型である場合、次の追加要件が適用されます。対応するメンバーの各ペアが互換性のある型で宣言され、対応するペアの 1 つのメンバーが名前で宣言されている場合、他のメンバーは同じ名前で宣言されています。2 つの構造体の場合、対応するメンバーは同じ順序で宣言する必要があります。


あなたがやりたい方法はうまくいきません。関数を呼び出すと、通常の代入と同様に、引数がパラメーターの型に変換されます。

したがって、これが機能するには、パラメーターの型と互換性のある引数が必要です。同じ翻訳単位で宣言された 2 つの共用体の場合、これは、それらの型が等しくなければならないことを意味します。これが、同じ翻訳単位内で互換性のある型を見つける唯一の方法です。しかし、名前のない共用体の宣言は一意の新しい型を作成するため、これは機能しません。別の宣言を使用してそれを「参照」する方法はありません。

要約すると、共用体型に名前を付ける必要があります。必要な基本引数を渡すために別の変数を作成することを避けるために、関数の外で宣言し、渡す可能性のある共用​​体を返す関数を作成します。

union base_type {
        uint16_t b16;
        uint32_t b32;
        uint64_t b64;
};

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz,
                  union base_type base,int self_running);

union base_type base_b16(uint16_t t) 
{ union base_type b; b.b16 = t; return b; }
union base_type base_b32(uint32_t t) 
{ union base_type b; b.b32 = t; return b; }
union base_type base_b64(uint64_t t) 
{ union base_type b; b.b64 = t; return b; }

今、それは次のようになります

pcg_new_state(...., base_b32(4211), ....);
于 2009-08-02T23:11:07.067 に答える
1

(編集: C++) 標準をざっと見てみたところ、8.3.5 - 関数 [dcl.fct] または 9.5 - 共用体 [class.union] のいずれにも、それを禁止するものは見当たりませんでした。ある程度の知識に基づいた推測として、ユニオンを渡すことは合法だと思いますが、そのようなインラインを宣言することはできません。GCC は以下を提供します。

エラー: パラメータの型で型が定義されていない可能性があります。

そのため、事前にタイプを定義する必要があります。

ただし、合法であっても、それが良い考えであるとは限りません。それを見てみると、過負荷がより良い解決策を提供する可能性があることをお勧めします。しかしもちろん、自分のコードを最もよく知っているのはあなたです...もっと単純で慣用的な解決策を探したいと思うかもしれません。

于 2009-08-02T22:05:14.900 に答える
0

関数でユニオンに名前を指定することで、これをGCCでコンパイルできますが、GCCが警告しているように、定義の範囲が原因で使用できません。

test_union.c:14: warning: ‘union X’ declared inside parameter list
test_union.c:14: warning: its scope is only this definition or declaration, which is probably not what you want

コード:

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz,
                         union X{
        uint16_t b16;
        uint32_t b32;
        uint64_t b64;
}base ,int self_running);
于 2009-08-02T22:18:59.610 に答える
0

とにかく、ユニオンのサイズは最大のメンバーのサイズになるため、何も得られません。より大きな符号なし型からより小さな符号なし型への変換は明確に定義されており、通常は実装が安価であるため、パラメーター uint64_t を作成することもできます。(たとえば、uint64_t を uint16_t に割り当てるには、幅の広い型の最下位の 16 ビットを取得するだけで実行できます)。

編集:例えば

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz, uint64_t param_base ,int self_running)
{
    /* Implementation for 16 bit arch */
    uint16_t base = param_base;

    /* ... more code ... */
}
于 2009-08-02T23:30:38.843 に答える
0

これはどう:

typedef struct _base
{
    union
    {
        uint16_t b16;
        uint32_t b32;
        uint64_t b64;
    };
}Base;

int func(Base b)
{
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{

    Base b;
    b.b16 = 0xFFFF;
    func(b);

    return 0;

}
于 2009-08-02T22:17:47.553 に答える