以下を印刷すると -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()intunsigned long long intint-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を格納する必要があります。largestIntegerInClimits.hULLONG_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. という警告が表示されます。