web-dev-qa-db-ja.com

c99にない構造体内の匿名結合?

ここに私が持っている問題の非常に簡略化されたコードがあります:

 enum node_type {
 t_int、t_double 
}; 
 
 struct int_node {
 int value; 
} ; 
 
 struct double_node {
 double value; 
}; 
 
 struct node {
 enum node_type type ; 
 union {
 struct int_node int_n; 
 struct double_node double_n; 
};
}; 
 
 int main(void){
 struct int_node i; 
 i.value = 10; 
 struct node n; 
 n.type = t_int; 
 n。int_n = i; 
 return 0; 
} 

そして、私が理解できないのはこれです:

 $ cc us.c 
 $ cc -std = c99 us.c 
 us.c:18:4:警告:宣言は何も宣言しません
 us .c:関数 'main':
 us.c:26:4:エラー: 'struct node'には 'int_n' 
という名前のメンバーがありません

-stdオプションなしでGCCを使用すると、上記のコードは問題なくコンパイルされます(同様のコードはかなりうまく機能します)が、c99はこの手法を許可しないようです。なぜそうであり、c99(またはc89c90)と互換性を持たせることができるのですか?ありがとう。

49
Martin

匿名の共用体はGNU拡張であり、C言語の標準バージョンの一部ではありません。-std= gnu99またはc99 + GNU拡張のようなものを使用できますが、適切に記述するのが最善ですCであり、構文上の砂糖だけを提供する拡張機能に依存しない...

編集: C11で匿名の共用体が追加されたため、これらは言語の標準部分になりました。おそらくGCCの-std=c11を使用できます。

62
R..

私はこの質問を他のみんなが1年半後に見つけたので、別の答えを出すことができます:匿名の構造体はC99標準ではなく、C11標準です。 GCCとclangは既にこれをサポートしています(C11標準はMicrosoftからこの機能を解除しているようで、GCCはしばらくの間MSFT拡張機能のサポートを提供してきました)。

25
bk.

解決策は、ユニオンのインスタンスに名前を付け(データ型として匿名のままにすることができます)、その名前をプロキシとして使用することでした。

 $ diff -u old_us.c us.c 
 --- old_us.c 2010-07-12 13:49:25.000000000 +0200 
 +++ us.c 2010 -07-12 13:49:02.000000000 +0200 
 @@ -15,7 +15,7 @@ 
 union {
 struct int_node int_n; 
 struct double_node double_n; 
-}; 
 +} data; 
}; 
 
 int main(void){
 @@ -23,6 +23,6 @@ 
 i.value = 10; 
 struct node n; 
 n.type = t_int; 
-n.int_n = i; 
 + n.data.int_n = i; 
 return 0; 
} 

今ではc99問題なく。

 $ cc -std = c99 us.c 
 $ 

注:とにかく、このソリューションには満足できません。

5
Martin

匿名のstructまたは匿名のunionに関する説明のためだけに。

C11

6.7.2.1構造およびユニオン指定子

型指定子がno tagを持つ構造指定子である名前のないメンバーanonymous structureと呼ばれます;型指定子がno tagを持つ共用体指定子である名前のないメンバーanonymous unionと呼ばれます。匿名構造体または共用体のメンバーは、包含構造体または共用体のメンバーと見なされます。これは、包含構造または共用体も匿名である場合、再帰的に適用されます。

C99匿名の構造体またはユニオンはありません

簡略化:Type-specifierIdentifier{宣言リスト}タグ;

  • タイプ指定子structまたはunion;
  • Identifier:オプション、structまたはunionのカスタム名。
  • Declaration-list:メンバー、変数、匿名structおよび匿名union
  • タグ:オプション。 Type-specifierの前にtypedefがある場合、Tagsはエイリアスであり、タグ

これは、識別子とタグがなく、別のstructまたはunion内に存在する場合のみ、匿名structまたは匿名unionです。

struct s {
    struct { int x; };     // Anonymous struct, no identifier and no tag
    struct a { int x; };   // NOT Anonymous struct, has an identifier 'a'
    struct { int x; } b;   // NOT Anonymous struct, has a tag 'b'
    struct c { int x; } C; // NOT Anonymous struct
};

struct s {
    union { int x; };     // Anonymous union, no identifier and no tag
    union a { int x; };   // NOT Anonymous union, has an identifier 'a'
    union { int x; } b;   // NOT Anonymous union, has a tag 'b'
    union c { int x; } C; // NOT Anonymous union
};

typedef地獄:typedefがある場合、タグ部分はもうタグではなく、そのタイプのエイリアスです。

struct a { int x; } A; // 'A' is a tag
union a { int x; } A;  // 'A' is a tag

// But if you use this way
typedef struct b { int x; } B; // 'B' is NOT a tag. It is an alias to struct 'b'
typedef union b { int x; } B;  // 'B' is NOT a tag. It is an alias to union 'b'

// Usage
A.x = 10; // A tag you can use without having to declare a new variable

B.x = 10; // Does not work

B bb; // Because 'B' is an alias, you have to declare a new variable
bb.x = 10;

次の例では、structunionに変更するだけで、同じように機能します。

struct a { int x; }; // Regular complete struct type
typedef struct a aa; // Alias 'aa' for the struct 'a'

struct { int x; } b; // Tag 'b'
typedef struct b bb; // Compile, but unusable.

struct c { int x; } C; // identifier or struct name 'c' and tag 'C'
typedef struct { int x; } d; // Alias 'd'
typedef struct e { int x; } ee; // struct 'e' and alias 'ee'

2

別の解決策は、共通のヘッダー値(enum node_type type)すべての構造体に追加し、最上位構造体を結合にします。正確に「自分自身を繰り返さない」というわけではありませんが、匿名の結合と不快な外観のプロキシ値の両方を回避します。

enum node_type {
    t_int, t_double
};
struct int_node {
    enum node_type type;
    int value;
};
struct double_node {
    enum node_type type;
    double value;
};
union node {
    enum node_type type;
    struct int_node int_n;
    struct double_node double_n;
};

int main(void) {
    union node n;
    n.type = t_int; // or n.int_n.type = t_int;
    n.int_n.value = 10;
    return 0;
}
1
theJPster

C99の6.2.7.1を見ると、識別子がオプションであることがわかりました。

struct-or-union-specifier:
    struct-or-union identifier-opt { struct-declaration-list }
    struct-or-union identifier

struct-or-union:
    struct
    union

struct-declaration-list:
    struct-declaration
    struct-declaration-list struct-declaration

struct-declaration:
    specifier-qualifier-list struct-declarator-list ;

specifier-qualifier-list:
    type-specifier specifier-qualifier-list-opt
    type-qualifier specifier-qualifier-list-opt

私はあちこち検索してきましたが、匿名の組合が仕様に反しているという言及を見つけることができません。 -optサフィックス全体は、この場合、identifierが6.1に従ってオプションであることを示します。

1
jer

ユニオンには名前があり、次のように宣言する必要があります。

union UPair {
    struct int_node int_n;
    struct double_node double_n;
};

UPair X;
X.int_n.value = 12;
1
Svisstack