2011-09-14 8 views
7
C99標準の私の案の

6.7.2.1項14これは労働組合とポインタについて言いたいことがある(いつものように強調し、追加):va_argを共用体で使用できますか?

The size of a union is sufficient to contain the largest of its members. The value of at most one of the members can be stored in a union object at any time. A pointer to a union object, suitably converted, points to each of its members (or if a member is a bit- field, then to the unit in which it resides), and vice versa.

すべてが順調といい、それはそれはに合法であることを意味しこれは言ってい

union ints { int i; unsigned u; }; 

int i = 4; 
union ints is = *(union ints *)&i; 
int j = is.i; // legal 
unsigned k = is.u; // not so much 

7.15.1.1項2:

私たちは、同じタイプのデータにそれをコピーしたいと仮定すると、労働組合に署名またはunsigned int型のいずれかをコピーするには、次のような何かを

The va_arg macro expands to an expression that has the specified type and the value of the next argument in the call. The parameter ap shall have been initialized by the va_start or va_copy macro (without an intervening invocation of the va_end macro for the sameap). Each invocation of the va_arg macro modifies ap so that the values of successive arguments are returned in turn. The parameter type shall be a type name specified such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type . If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:

—one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;

—one type is pointer to void and the other is a pointer to a character type.

私は、デフォルトの引数のプロモーションについてこの部分を引用するつもりはありません。私の質問は次のとおりです。

void func(int i, ...) 
{ 
    va_list arg; 
    va_start(arg, i); 
    union ints is = va_arg(arg, union ints); 
    va_end(arg); 
} 

int main(void) 
{ 
    func(0, 1); 
    return 0; 
} 

もしそうなら、克服する巧妙なトリックであるように思われる「とし、値が型の両方と互換性があります」(中とはいえ署名/符号なし整数変換の要件:この定義された動作です法的に何かをするのはむしろ難しい方法です)。そうでない場合は、この場合はunsignedを使用するだけで安全ですが、より互換性のないタイプの要素が多い場合はどうなりますか?私たちが要素ごとにユニオンにアクセスしないことを保証できれば(すなわち、別のunionまたはそれと同じ扱いのストレージスペースにコピーするだけです)、ユニオンのすべての要素が同じサイズであればvarargsと?またはポインタでのみ許可されますか?

実際には、このコードはほとんど決して失敗しないだろうと期待していますが、定義済みの動作かどうかを知りたいと思います。私の現在の推測は、それは定義されていないようだが、それは信じられないほどばかげているようだ。

+1

私はそれについて考えました。私は、func(0、-1); func(0、UINT_MAX); 'は合法です。 '1 'は' int'と 'unsigned'の両方に収まるので、' func(0、1) 'は正当なものです。 –

+0

'unsigned k = is.u;'はC99では合法です。 –

+0

@Dietrich - 署名付き/署名なしのもののため例外がありますか? –

答えて

0

実際にはコードが決して失敗しないと思う理由はわかりません。整数型がレジスタで渡される実装では失敗しますが、スタック型では集約型が渡されますが、そのような実装を禁止する標準は何もありません。 intを含む共用体は、サイズが同じであっても、intと互換性のある型ではありません。

戻るあなたの最初のコードの断片に、それはあまりにも問題があります。

union ints is = *(union ints *)&i; 

これは、エイリアシング違反であると未定義の動作を呼び出します。私もここにあなたのコメントについて少し混乱しています。..

をあなたはmemcpyを使用してそれを避けることができ、私はそれが合法だろうとします

unsigned k = is.u; // not so much 

値4が署名の両方で表現されているので特別な場合として特別に禁止されていない限り、これは正当なものでなければなりません。

これがあなたの質問に答えることができない場合は、あなたが解決しようとしている問題(理論的ではあるが)についてもっと詳しく説明することができます。

+0

これがエイリアシング違反の仕組みを広げることができますか? – wnoise

+0

まず、オブジェクトを 'int'型の左辺値から' union ints'型の1つを介してアクセスします。コンパイラは、別名ではないと仮定します。 –

+0

コンパイラは、省略記号( '...')の代わりに渡されるパラメータで 'restrict'を引き受けることを許可されていますか? –

3

あなたにはいくつかの問題があります。

A pointer to a union object, suitably converted, points to each of its members (or if a member is a bit- field, then to the unit in which it resides), and vice versa.

これは、種類が互換性があるわけではありません。実際、彼らは互換性がありません。したがって、次のコードが間違っている:

func(0, 1); // undefined behavior 

あなたが組合を渡したい場合は、

func(0, (union ints){ .u = BLAH }); 

あなたがコードを書くことで確認することができ、

union ints x; 
x = 1; 

GCCは、「エラーを与えます:コンパイル時の「割り当てに互換性のない型」メッセージが表示されます。

しかし、ほとんどの実装はどちらの場合でも「おそらく」正しいことを行います。他のいくつかの問題があります...

union ints { 
    int i; 
    unsigned u; 
}; 

int i = 4; 
union ints is = *(union ints *)&i; // Invalid 
int j = is.i; // legal 
unsigned k = is.u; // also legal (see note) 

その実際の型 *(uinon ints *)&i以外のタイプを使用したタイプのアドレスは時々未定義で逆参照します(参照を見上げ、私はおよそかなり確信している行動この)。しかし、C99では、最も最近格納された共用体メンバ以外の(またはC1x?)以外の共用体メンバにアクセスすることは許可されていますが、値は実装定義であり、トラップ表現である可能性があります。

ユニオンを使用した型打ちについて: Pascal Cuoqは、実際には、最近格納されたもの以外の共用体要素にアクセスする動作を定義するTC3です。 TC3はC99の3番目のアップデートです。良い知らせは、TC3のこの部分が実際に既存の練習をコード化しているということです。それをTC3より前のCの事実上の部分と考えてください。

+3

TC3は、C99で型のパニングに使用できることを明示しています。欠陥レポート283 http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm –

+0

@Pascal:Nice to知っている! –

2

標準が言うので:union intsについては

The parameter type shall be a type name specified such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type.

を、その条件が満たされています。 union ints *union intsへのポインタを完全に適切に表現しているので、その文にユニオンとしてスタックにプッシュされた値を収集するのに使用されないようには何もありません。

ユニオンの代わりにintまたはunsigned intを渡すと、未定義の動作が発生します。このように、あなたが使用できます。

union ints u1 = ...; 

func(0, (union ints) { .i = 0 }); 
func(1, (union ints) { .u = UINT_MAX }); 
func(2, u1); 

あなたが使うことができませんでした:

func(1, 0); 

引数が労働組合のタイプではありません。

関連する問題