理解する必要がある 2 つのことが起こっているようです。
さらに、出力を理解するのに役立つ 2 つの点があります。
まず、printf()
可変関数です。引数の型が (フォーマット文字列以外で) わからないため、変換指定子を使用して引数の解釈方法を指定する必要があります。これらの引数は、3 ビットのビット フィールドが s に昇格されるように、「デフォルトの引数昇格」の対象となりますint
。
データの符号と一致しない変換指定子 ( %d
、%u
、および%d
) を使用しているため、メモリ内でのデータの実際の表現方法に依存する未定義の動作が発生します。
次に、C11 標準は次のように述べています。
6.3.1.3 符号付きおよび符号なし整数
整数型の値が _Bool 以外の別の整数型に変換される場合、その値が新しい型で表現できる場合、その値は変更されません。
それ以外の場合、新しい型が符号なしの場合、値が新しい型の範囲内になるまで、新しい型で表現できる最大値よりも 1 多い値を繰り返し加算または減算することによって、値が変換されます。
それ以外の場合、新しい型は署名され、値を表現できません。結果が実装定義であるか、実装定義のシグナルが発生します。
(私が知る限り、ここに関連する詳細は、少なくとも C89 以降は真実です。)
これにより、コードに関するいくつかのことがわかります。
したがって、未定義の動作と実装定義の動作の両方がありますが、楽しみのために、出力を理解しようとすることができます。32 ビット整数と2 の補数表現を想定しています。
お使いのシステムは、 に4294967295
保存されている をx
として表し11111111 11111111 11111111 11111111
ます。printf()
渡した引数が符号付きであると伝えた場合、それらの同じビットは-1
、得られた出力である として解釈されます。
の場合s.c
、取得したと思われる実装定義の動作は簡単です。3 ビット101
を表す3 ビットはそのまま5
格納されます。つまり、正しい変換指定子を使用すると、として表示されるprintf()
はずです。s.c
-3
割り当てた値は次のとおりです。
s.i = 101
s.c = 101
x = 11111111 11111111 11111111 11111111
0
3 ビット値は、符号なし値の左パディングと符号付き値の符号の繰り返しによって 32 ビットに昇格されます。
s.i = 00000000 00000000 00000000 00000101
s.c = 11111111 11111111 11111111 11111101
x = 11111111 11111111 11111111 11111111
符号付き、符号なし、および符号付き整数として解釈されると、次のようになります。
s.i=5
s.c=4294967293
x=-1
は、実際には 2のx=-1
補数表現を使用していることを示唆しています (とにかく、これはかなり安全な賭けでした)。 の出力は、 s が 32 ビット幅であることをs.c
示唆しています。int