web-dev-qa-db-ja.com

型変換-符号なしから符号付きint / char

私は以下のプログラムを実行しようとしました:

#include <stdio.h>

int main() {
    signed char a = -5;
    unsigned char b = -5;
    int c = -5;
    unsigned int d = -5;

    if (a == b)
        printf("\r\n char is SAME!!!");
    else
        printf("\r\n char is DIFF!!!");

    if (c == d)
        printf("\r\n int is SAME!!!");
    else
        printf("\r\n int is DIFF!!!");

    return 0;
}

このプログラムの場合、出力を取得しています:

charはDIFF !!! intは同じです!!!

なぜ両方の出力が異なるのですか?
出力は次のようになりますか?

charは同じです!!! intは同じです!!!

A コードパッドリンク

73
user2522685

これは、Cのさまざまな暗黙の型変換規則によるものです。Cプログラマが知っておく必要のある2つの規則があります。通常の算術変換および整数プロモーション(後者は前者の一部です)。

Charの場合、タイプは(signed char) == (unsigned char)です。これらは両方とも小整数型です。他のそのような小さな整数型はboolshortです。 整数プロモーションルールは、小さな整数型が操作のオペランドであるときはいつでも、そのタイプがintに昇格することを示します。署名されています。これは、タイプが署名されているかどうかに関係なく発生します。

signed charの場合、符号は保持され、値-5を含むintに昇格されます。 unsigned charの場合、251(0xFB)の値が含まれます。同じ値を含むintに昇格されます。で終わる

if( (int)-5 == (int)251 )

整数の場合、タイプは(signed int) == (unsigned int)です。これらは短整数型ではないため、整数の昇格は適用されません。代わりに、それらは通常の算術変換によってバランスが取られます。これは、2つのオペランドが同じ「ランク」(サイズ)であるが符号が異なる場合、符号付きオペランドは、符号なしのものと同じ型に変換されます。で終わる

if( (unsigned int)-5 == (unsigned int)-5)
81
Lundin

いい質問です!

int比較は機能します。これは、両方のintにまったく同じビットが含まれているため、本質的に同じであるためです。しかし、charsはどうですか?

ああ、Cはさまざまな場面でcharsをintsに暗黙的に昇格させます。これはそれらの1つです。コードにはif(a==b)と書かれていますが、コンパイラーが実際に変換するのは次のとおりです。

if((int)a==(int)b) 

(int)aは-5ですが、(int)bは251です。これらは間違いなく同じではありません。

編集:@ Carbonic-Acidが指摘したように、(int)bは、charが8ビット長の場合のみ251です。 intが32ビット長の場合、(int)bは-32764です。

REDIT:バイトが8ビット長でない場合の答えの性質について議論するコメントがたくさんあります。この場合の唯一の違いは、(int)bは251ではなく、異なる数であり、-5ではありません。これはまだ非常にクールな質問とは関係ありません。

36
zmbq

整数プロモーション へようこそ。ウェブサイトから引用できる場合:

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

これらのような比較を行うと、Cは非常に混乱する可能性があります。最近、C以外のプログラミングの友人の一部を次のように困惑させました。

#include <stdio.h>
#include <string.h>

int main()
{
    char* string = "One looooooooooong string";

    printf("%d\n", strlen(string));

    if (strlen(string) < -1) printf("This cannot be happening :(");

    return 0;
}

確かにThis cannot be happening :(そして一見25は-1より小さいことを示しています!

ただし、その下で何が起こるかは、-1が符号なし整数として表されることです。これは、基になるビット表現のために、32ビットシステムでは4294967295に等しくなります。そして当然、25は4294967295よりも小さいです。

ただし、size_tstrlenによって符号付き整数として返される型:

if ((int)(strlen(string)) < -1)

その後、25と-1を比較し、すべてが世界とうまくいきます。

優れたコンパイラーは、符号なし整数と符号付き整数の比較について警告する必要がありますが、それでも見逃しやすい(特に警告を有効にしていない場合)。

これは、Javaすべてのプリミティブ型としてのプログラマーが署名されているため、特に混乱を招きます。JamesGosling(Javaの作成者の1人) 主題について言わなければなりませんでした

ゴスリング:言語デザイナーとしての私にとって、私は最近自分自身を数えていませんが、「シンプル」とは本当に意味があったのは、J。Random Developerが彼の頭の中に仕様を保持することでした。その定義では、たとえばJavaはそうではありません。実際、これらの言語の多くは、実際には誰も理解していない多くのコーナーケースを抱えています。符号なしについて、そしてすぐに、符号なしで何が起こっているのか、符号なし算術が何であるかを実際に理解しているC開発者はほとんどいないことがわかります。そのようなことがCを複雑にしました。Java is、Iあなたが調べなければならないライブラリ。

21
Nobilis

-5の16進表現は次のとおりです。

  • 8ビット、2の補数signed char0xfb
  • 32ビット、2の補数signed int0xfffffffb

符号付きの数値を符号なしの数値に、またはその逆に変換すると、コンパイラーは...まったく何もしません。どうすればいいですか?数値は変換可能かそうでないかのどちらかです。その場合、未定義または実装定義の動作が続き(実際にはどちらをチェックしたかはわかりません)、最も効率的な実装定義の動作は何もしないことです。

したがって、(unsigned <type>)-5の16進表現は次のとおりです。

  • 8ビット、unsigned char0xfb
  • 32ビット、unsigned int0xfffffffb

見覚えがあります?それらは、署名されたバージョンとビットごとに同じです。

abchar型であるif (a == b)を記述する場合、コンパイラが実際に読み取る必要があるのはif ((int)a == (int)b)です。 (これは、他の誰もがやっつけている「整数プロモーション」です。)

charintに変換するとどうなりますか?

  • 8ビットsigned charから32ビットsigned int0xfb-> 0xfffffffb
    • さて、それは上記の-5の表現と一致するため、理にかなっています!
    • これは、バイトの最上位ビットである「符号ビット」を新しいより広い値に左方向にコピーするため、「符号拡張」と呼ばれます。
  • 8ビットunsigned charから32ビットsigned int0xfb-> 0x000000fb
    • 今回は、ソースタイプがnsignedであるため、「ゼロ拡張」を実行するため、コピーする符号ビットがありません。

したがって、a == bは本当に0xfffffffb == 0x000000fb =>一致しません!

そして、c == dは本当に0xfffffffb == 0xfffffffb =>マッチします!

10
ams

私の要点は、コンパイル時に「署名された式と署名されていない式を比較する」という警告が表示されなかったことですか?

コンパイラは、クレイジーなことをする権利があることを通知しようとしています! :)プリミティブ型の容量に近い大きな値を使用すると、クレイジーなことが起こります。そして

 unsigned int d = -5;

は、dに大きな値を確実に割り当てています。これは、(同等であるとは限りませんが)同等です:

 unsigned int d = UINT_MAX -4; ///Since -1 is UINT_MAX

編集:

ただし、2番目の比較でのみ警告が表示されることに注意してください。 (コードをチェック) 。したがって、変換ルールを適用するコンパイラーは、unsigned charcharの比較にエラーがないと確信していることを意味します(比較中に、それらはすべてを安全に表現できる型に変換されますその可能な値)。そして、彼はこの点に関して正しいです。次に、これはunsigned intintには当てはまらないことを通知します:比較中に2つのうちの1つは完全に表現できない型に変換されます。

完全を期すために、 略して確認しました :コンパイラはcharsと同じように動作し、予想どおり、実行時にエラーは発生しません。

このトピックに関連して、最近 この質問 (まだC++指向)を尋ねました。

1
Antonio