私は以下のプログラムを実行しようとしました:
#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 コードパッドリンク 。
これは、Cのさまざまな暗黙の型変換規則によるものです。Cプログラマが知っておく必要のある2つの規則があります。通常の算術変換および整数プロモーション(後者は前者の一部です)。
Charの場合、タイプは(signed char) == (unsigned char)
です。これらは両方とも小整数型です。他のそのような小さな整数型はbool
とshort
です。 整数プロモーションルールは、小さな整数型が操作のオペランドであるときはいつでも、そのタイプが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)
いい質問です!
int
比較は機能します。これは、両方のintにまったく同じビットが含まれているため、本質的に同じであるためです。しかし、char
sはどうですか?
ああ、Cはさまざまな場面でchar
sをint
sに暗黙的に昇格させます。これはそれらの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ではありません。これはまだ非常にクールな質問とは関係ありません。
整数プロモーション へようこそ。ウェブサイトから引用できる場合:
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_t
strlen
によって符号付き整数として返される型:
if ((int)(strlen(string)) < -1)
その後、25と-1を比較し、すべてが世界とうまくいきます。
優れたコンパイラーは、符号なし整数と符号付き整数の比較について警告する必要がありますが、それでも見逃しやすい(特に警告を有効にしていない場合)。
これは、Javaすべてのプリミティブ型としてのプログラマーが署名されているため、特に混乱を招きます。JamesGosling(Javaの作成者の1人) 主題について言わなければなりませんでした :
ゴスリング:言語デザイナーとしての私にとって、私は最近自分自身を数えていませんが、「シンプル」とは本当に意味があったのは、J。Random Developerが彼の頭の中に仕様を保持することでした。その定義では、たとえばJavaはそうではありません。実際、これらの言語の多くは、実際には誰も理解していない多くのコーナーケースを抱えています。符号なしについて、そしてすぐに、符号なしで何が起こっているのか、符号なし算術が何であるかを実際に理解しているC開発者はほとんどいないことがわかります。そのようなことがCを複雑にしました。Java is、Iあなたが調べなければならないライブラリ。
-5
の16進表現は次のとおりです。
signed char
:0xfb
signed int
:0xfffffffb
符号付きの数値を符号なしの数値に、またはその逆に変換すると、コンパイラーは...まったく何もしません。どうすればいいですか?数値は変換可能かそうでないかのどちらかです。その場合、未定義または実装定義の動作が続き(実際にはどちらをチェックしたかはわかりません)、最も効率的な実装定義の動作は何もしないことです。
したがって、(unsigned <type>)-5
の16進表現は次のとおりです。
unsigned char
:0xfb
unsigned int
:0xfffffffb
見覚えがあります?それらは、署名されたバージョンとビットごとに同じです。
a
とb
がchar
型であるif (a == b)
を記述する場合、コンパイラが実際に読み取る必要があるのはif ((int)a == (int)b)
です。 (これは、他の誰もがやっつけている「整数プロモーション」です。)
char
をint
に変換するとどうなりますか?
signed char
から32ビットsigned int
:0xfb
-> 0xfffffffb
-5
の表現と一致するため、理にかなっています!unsigned char
から32ビットsigned int
:0xfb
-> 0x000000fb
したがって、a == b
は本当に0xfffffffb == 0x000000fb
=>一致しません!
そして、c == d
は本当に0xfffffffb == 0xfffffffb
=>マッチします!
私の要点は、コンパイル時に「署名された式と署名されていない式を比較する」という警告が表示されなかったことですか?
コンパイラは、クレイジーなことをする権利があることを通知しようとしています! :)プリミティブ型の容量に近い大きな値を使用すると、クレイジーなことが起こります。そして
unsigned int d = -5;
は、dに大きな値を確実に割り当てています。これは、(同等であるとは限りませんが)同等です:
unsigned int d = UINT_MAX -4; ///Since -1 is UINT_MAX
編集:
ただし、2番目の比較でのみ警告が表示されることに注意してください。 (コードをチェック) 。したがって、変換ルールを適用するコンパイラーは、unsigned char
とchar
の比較にエラーがないと確信していることを意味します(比較中に、それらはすべてを安全に表現できる型に変換されますその可能な値)。そして、彼はこの点に関して正しいです。次に、これはunsigned int
とint
には当てはまらないことを通知します:比較中に2つのうちの1つは完全に表現できない型に変換されます。
完全を期すために、 略して確認しました :コンパイラはcharsと同じように動作し、予想どおり、実行時にエラーは発生しません。
。
このトピックに関連して、最近 この質問 (まだC++指向)を尋ねました。