14

次の例を検討してください。

#include <stdio.h>

int main(void)
{
    unsigned char a  = 15; /* one byte */
    unsigned short b = 15; /* two bytes */
    unsigned int c   = 15; /* four bytes */

    long x = -a; /* eight bytes */
    printf("%ld\n", x);

    x = -b;
    printf("%ld\n", x);

    x = -c;
    printf("%ld\n", x);

    return 0;
}

コンパイルするには、GCC 4.4.7 を使用しています (警告は表示されませんでした)。

gcc -g -std=c99 -pedantic-errors -Wall -W check.c

私の結果は次のとおりです。

-15
-15
4294967281

問題は、 と の両方unsigned charの値が (signed)にunsigned short正しく「伝播」され、そうでないのはなぜですか? これに関する参照またはルールはありますか?longunsigned int

gdbそれに応じて(単語はリトルエンディアン順です)からの結果は次のとおりです。

(gdb) x/2w &x
0x7fffffffe168: 11111111111111111111111111110001    11111111111111111111111111111111 

(gdb) x/2w &x
0x7fffffffe168: 11111111111111111111111111110001    00000000000000000000000000000000
4

5 に答える 5

12

これは、整数昇格がオペランドに適用される方法と、単項マイナスの結果が同じ型であるという要件によるものです。これは6.5.3.3 単項算術演算子のセクションで説明されており、次のように述べています(私のものを強調します):

単項 - 演算子の結果は、その (昇格された) オペランドの否定です。オペランドに対して整数の昇格が実行され、結果の型は昇格されます。

ドラフトc99標準セクション6.3 コンバージョンでカバーされ、次のように述べている整数プロモーション:

int が元の型のすべての値を表すことができる場合、値は int に変換されます。それ以外の場合は、unsigned int に変換されます。これらは整数プロモーションと呼ばれます。48)他のすべての型は、整数の昇格によって変更されません。

最初の 2 つのケースでは、昇格はintになり、結果はintになります。unsigned intの場合、昇格は必要ありませんが、結果をunsigned intに戻す必要があります。

は、セクションSigned and unsigned integersで説明されているルールを使用してunsigned int-15に変換されます。6.3.1.3

それ以外の場合、新しい型が符号なしの場合、値が新しい型の範囲内になるまで、新しい型で表現できる最大値よりも 1 多い値を繰り返し加算または減算することによって、値が変換されます。49)

したがって、-15 + (UMAX + 1)どの結果がUMAX - 14どの結果になり、どの結果が大きな符号なしの値になります。-1これが、型の最大符号なし値を取得するために符号なし値に変換されたコードを使用することがある理由-1 + UMAX + 1ですUMAX

于 2014-06-02T12:28:25.157 に答える
3

int特別です。より小さいものはすべて、算術演算でint昇格されます。int

したがって-a、 と-bは単項マイナスをint15 の値に適用したもので、機能して -15 を生成します。この値は に変換されlongます。

-c異なります。はよりも小さくないcため、 に昇格されません。の値に単項マイナスを適用した結果は、再び であり、2 N -k (N はビット数)として計算されます。intintunsigned intkunsigned int

これで、このunsigned int値はlong正常に変換されます。

于 2014-06-02T12:40:48.673 に答える
2

C の整数昇格規則は、標準の作成者がさまざまなことを行うさまざまな既存の実装を許可したかったためです。場合によっては、「標準」が存在する前に作成されたため、彼らが行っていたことをやり続けることができます。 「好きなことをする」よりも具体的な新しい実装のルールを定義します。残念ながら、書かれているルールでは、コンパイラの整数サイズに依存しないコードを書くことは非常に困難です。int将来のプロセッサが 32 ビットの操作よりも 64 ビットの操作を高速に実行できるようになったとしても、標準によって定められた規則により、 32 ビットを超えた場合、多くのコードが壊れる可能性があります。

振り返ってみると、C の複数の方言の存在を明示的に認識し、コンパイラーがさまざまなことを一貫した方法で処理する方言を実装することを推奨することによって、「奇妙な」コンパイラーを処理したほうがよかったでしょう。それらを別の方法で行います。このようなアプローチは、最終的にint32 ビットを超えて拡張できる唯一の方法になる可能性がありますが、そのようなことを検討している人は聞いたことがありません。

符号なし整数型の問題の根源は、数値を表すために使用されることもあれば、ラップする抽象代数環のメンバーを表すために使用されることもあるという事実にあると思います。符号なしの型は、型の昇格を伴わない状況では、抽象代数環と一致する方法で動作します。単項マイナスを環のメンバーに適用すると、同じ環のメンバーが生成され、元の環に加算するとゼロになります [つまり、加法逆数]。整数量を環要素にマッピングする方法は 1 つだけですが、環要素を整数量にマッピングする方法は複数あります。したがって、リング要素を整数量に追加すると、同じリングの要素が生成されますinteger のサイズに関係なく、リングから整数量への変換では、変換の実行方法をコードで指定する必要があります。残念ながら、リングのサイズがデフォルトの整数型よりも小さい場合、または操作でより大きな型の整数を持つリング メンバーを使用する場合、C は暗黙的にリングを整数に変換します。

この問題を解決するための適切な解決策は、特定の変数、戻り値などを数値ではなくリング型と見なすようにコードで指定できるようにすることです。like の式は、が 16 ビットのシステムで 65534 を生成し、より大きなシステムで -2 を生成するのではなく-(ring16_t)2、 のサイズに関係なく 65534 を生成する必要があります。同様に、たまたま 64 ビットであっても生成する必要があります [ 64 ビットの場合、コードが 0xC0000001 に等しい 2 つの符号なし 32 ビット値を乗算しようとすると、コンパイラは好きなことを合法的に実行できることに注意してください。 64 ビットの符号付き整数で表します。intint(ring32)0xC0000001 * (ring32)0xC0000001(ring32)0x80000001intint

于 2014-06-02T17:59:35.817 に答える
0

ネガは厄介です。特に符号なしの値に関しては。c-documentation を見ると、(予想に反して) unsigned char と short は計算のために signed int に昇格され、unsigned int は unsigned int として計算されることがわかります。

-c を計算すると、c は int として扱われ、-15 になり、x に格納され (これはまだ UNSIGNED int であると認識されています)、そのように格納されます。

明確化のために - 署名なしを「否定」する場合、実際の昇格は行われません。任意の型の int に負数を割り当てる (または負数を取る) と、代わりに数値の 2 の補数が使用されます。符号なしの値と符号付きの値の実際的な違いは、MSB が符号フラグとして機能することだけであるため、負の値ではなく、非常に大きな正の値と見なされます。

于 2014-06-02T12:28:21.487 に答える