2010-02-16 3 views
2

Почему я получаю -1, когда печатаю следующее?Печать unsigned long long с использованием% d

unsigned long long int largestIntegerInC = 18446744073709551615LL; 

printf ("largestIntegerInC = %d\n", largestIntegerInC); 

Я знаю, что я должен использовать llu вместо d, но почему я получаю -1 вместо 18446744073709551615LL?

Это из-за переполнения?

+0

Все ответы ниже - это просто спекуляция. Это просто ** неопределенное поведение **, где разрешено что-либо. – Jens

ответ

4

В C (99), LLONG_MAX, максимальное значение long long int должно быть не менее 9223372036854775807. Максимальное значение unsigned long long int должно быть не менее 18446744073709551615, что составляет 2 − 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’ 

Неверный второй printf(), он может распечатать что угодно. Вы используете "%d", что означает, что 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 в вашей 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 и next содержат наибольшее возможное значение для unsigned long long int типа.

1

В арифметике дополнений 2, значение знака -1 совпадает с наибольшим значением без знака.

Рассмотрим битовые шаблоны для отрицательных чисел в дополнительном коде (я использую 8-битовые целые числа, но шаблон применяется независимо от размера):

0 - 0x00 
-1 - 0xFF 
-2 - 0xFE 
-3 - 0xFD 

Таким образом, вы можете видеть, что отрицательный 1 имеет битовый шаблон всех 1, который также является битовой диаграммой для наибольшего значения без знака.

0

Вы использовали формат для подписанного 32-разрядного номера, поэтому вы получили -1. printf() не может внутренне определить, насколько велика ваша сумма, поэтому она просто извлекает первые 32 бита из списка varargs и использует их как значение, которое нужно распечатать. Так как вы дали подписанный формат, он печатает его таким образом, а 0xffffffff - это представление двух дополнений в -1.

3

Это потому, что вы передаете число со всеми битами, установленными в 1. Когда интерпретируется как подписанное число дополнений двух, это работает до -1. В этом случае, вероятно, он смотрит только на 32 из этих бит, а не на все 64, но это не имеет никакого реального значения.

0

Вы можете (должны) понять, почему в предупреждении компилятора. Если нет, попробуйте установить самый высокий уровень предупреждения. С VS у меня есть это предупреждение: предупреждение C4245: 'initializing': преобразование из '__int64' в 'unsigned __int64', несоответствие с подписью/без знака.

0

Нет, нет переполнения. Это потому, что он не печатает всего значения:

18446744073709551615 - это то же самое, что и 0xFFFFFFFFFFFFFFFF. Когда printf %d обрабатывает это, он захватывает только 32 бита (или 64 бита, если это 64-разрядный ЦП) для преобразования, и это знаковое значение -1.

Если преобразование printf было %u, то оно отображало либо 4294967295 (32 бит), либо 18446744073709551615 (64 бит).

Переполнение происходит, когда значение увеличивается до точки, где оно не будет помещаться в выделенное хранилище. В этом случае значение выделено просто отлично, но не полностью получено.