以下を印刷すると -1 になるのはなぜですか?
unsigned long long int largestIntegerInC = 18446744073709551615LL;
printf ("largestIntegerInC = %d\n", largestIntegerInC);
llu
の代わりに使用する必要があることはわかってd
いますが、18446744073709551615LL の代わりに -1 が返されるのはなぜですか?
オーバーフローのせいですか?
以下を印刷すると -1 になるのはなぜですか?
unsigned long long int largestIntegerInC = 18446744073709551615LL;
printf ("largestIntegerInC = %d\n", largestIntegerInC);
llu
の代わりに使用する必要があることはわかってd
いますが、18446744073709551615LL の代わりに -1 が返されるのはなぜですか?
オーバーフローのせいですか?
C(99)ではLLONG_MAX
、typeの最大値はlong long int
少なくとも。であることが保証されています9223372036854775807
。anの最大値は、unsigned long long int
少なくとも18446744073709551615
、2 64 -1(0xffffffffffffffff
)であることが保証されています。
したがって、初期化は次のようになります。
unsigned long long int largestIntegerInC = 18446744073709551615ULL;
(。に注意してくださいULL
。)largestIntegerInC
はタイプなのでunsigned long long int
、正しいフォーマット指定子を使用して印刷する必要があります"%llu"
。
$ cat test.c
#include <stdio.h>
int main(void)
{
unsigned long long int largestIntegerInC = 18446744073709551615ULL;
/* good */
printf("%llu\n", largestIntegerInC);
/* bad */
printf("%d\n", largestIntegerInC);
return 0;
}
$ gcc -std=c99 -pedantic test.c
test.c: In function ‘main’:
test.c:9: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘long long unsigned int’
上記の2番目printf()
は間違っています、それは何でも印刷できます。を使用しています。これは、を期待している"%d"
ことを意味しますが、を取得します。これは(ほとんどの場合)と同じサイズではありません。出力として取得する理由は、(不運な)運と、マシン上で2の補数表現を使用して数値が表されるという事実によるものです。printf()
int
unsigned long long int
int
-1
これがどのように悪いかを確認するために、次のプログラムを実行してみましょう。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(int argc, char *argv[])
{
const char *fmt;
unsigned long long int x = ULLONG_MAX;
unsigned long long int y = 42;
int i = -1;
if (argc != 2) {
fprintf(stderr, "Need format string\n");
return EXIT_FAILURE;
}
fmt = argv[1];
printf(fmt, x, y, i);
putchar('\n');
return 0;
}
私のMacbookでは、でプログラムを実行すると"%d %d %d"
が得-1 -1 42
られ、Linuxマシンでは、同じ形式の同じプログラムでが得られます-1 42 -1
。おっと。
実際、変数に最大unsigned long long int
数を格納しようとしている場合は、を含めて使用する必要があります。または、変数にassingを格納する必要があります。largestIntegerInC
limits.h
ULLONG_MAX
-1
#include <limits.h>
#include <stdio.h>
int main(void)
{
unsigned long long int largestIntegerInC = ULLONG_MAX;
unsigned long long int next = -1;
if (next == largestIntegerInC) puts("OK");
return 0;
}
上記のプログラムでは、との両方largestIntegerInC
にtypenext
の可能な最大値が含まれていますunsigned long long int
。
これは、すべてのビットが 1 に設定された数値を渡しているためです。2 の補数の符号付き数値として解釈されると、-1 になります。この場合、おそらくこれらの 1 ビットのうち 64 ビットすべてではなく 32 ビットしか見ていませんが、実際の違いはありません。
2 の補数演算では、符号付きの値 -1 は符号なしの最大値と同じです。
2 の補数の負の数のビット パターンを考えてみましょう (私は 8 ビット整数を使用していますが、パターンはサイズに関係なく適用されます)。
0 - 0x00
-1 - 0xFF
-2 - 0xFE
-3 - 0xFD
したがって、負の 1 にはすべて 1 のビット パターンがあり、これは最大の符号なし値のビット パターンでもあることがわかります。
いいえ、オーバーフローはありません。これは、値全体を出力していないためです。
18446744073709551615は0xFFFFFFFFFFFFFFFFと同じです。それを処理するときprintf %d
、変換のために32ビット(または64ビットCPUの場合は64ビット)のみを取得し、それらは符号付きの値-1です。
printf
代わりに変換が行われた場合は%u
、4294967295(32ビット)または18446744073709551615(64ビット)のいずれかが表示されます。
オーバーフローとは、値が増加して、割り当てられたストレージに収まらない場合です。この場合、値は適切に割り当てられますが、完全には取得されていません。
符号付き 32 ビット数値の形式を使用したため、-1 が得られました。 printf()
渡された数値の大きさを内部的に判断できないため、varargs リストから最初の 32 ビットを取り出して、出力する値として使用します。署名された形式を指定したため、そのように出力され、0xffffffff は -1 の 2 の補数表現です。
コンパイラの警告で理由を確認できます (すべきです)。そうでない場合は、最高の警告レベルを設定してみてください。VS では、警告 C4245: 'initializing' : conversion from '__int64' to 'unsigned __int64', signed/unsigned mismatch. という警告が表示されます。