Pergunta

Por que recebo -1 quando imprimir o seguinte?

unsigned long long int largestIntegerInC = 18446744073709551615LL;

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

Eu sei que devo usar llu ao invés de d, mas por que eu recebo -1 em vez de 18446744073709551615ll?

É por causa do transbordamento?

Foi útil?

Solução

Em C (99), LLONG_MAX, o valor máximo de long long int Tipo é garantido pelo menos 9223372036854775807. O valor máximo de um unsigned long long int é garantido ser pelo menos 18446744073709551615, que é 264−1 (0xffffffffffffffff).

Portanto, a inicialização deve ser:

unsigned long long int largestIntegerInC = 18446744073709551615ULL;

(Note o ULL.) Desde largestIntegerInC é do tipo unsigned long long int, você deve imprimi -lo com o especificador de formato certo, que é "%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’

O segundo printf() Acima está errado, pode imprimir qualquer coisa. Você está usando "%d", que significa printf() está esperando um int, mas recebe um unsigned long long int, que é (provavelmente) não do mesmo tamanho que int. A razão pela qual você está recebendo -1 Como sua saída se deve à sorte (ruim) e ao fato de que, em sua máquina, os números são representados usando a representação do complemento de dois.


Para ver como isso pode ser ruim, vamos executar o seguinte programa:

#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;
}

No meu MacBook, executando o programa com "%d %d %d" me dá -1 -1 42, e em uma máquina Linux, o mesmo programa com o mesmo formato me dá -1 42 -1. Opa.


De fato, se você está tentando armazenar o maior unsigned long long int número em seu largestIntegerInC variável, você deve incluir limits.h E use ULLONG_MAX. Ou você deve armazenar Assing -1 para sua variável:

#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;
}

No programa acima, ambos largestIntegerInC e next conter o maior valor possível para unsigned long long int modelo.

Outras dicas

É porque você está passando um número com todos os bits definidos para 1. Quando interpretados como o número de dois complemento de dois, que resulta em -1. Nesse caso, provavelmente está apenas olhando para 32 desses bits em vez de todos os 64, mas isso não faz nenhuma diferença real.

Na aritmética do complemento de dois, o valor assinado -1 é o mesmo que o maior valor não assinado.

Considere os padrões de bits para números negativos no complemento de dois (estou usando números inteiros de 8 bits, mas o padrão se aplica independentemente do tamanho):

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

Portanto, você pode ver que o negativo 1 tem o padrão de bits de todos os 1, que também é o padrão de bits para o maior valor não assinado.

Você usou um formato para um número de 32 bits assinado, para ter -1. printf() Não sei dizer internamente o tamanho do número que você passou, por isso apenas puxa os primeiros 32 bits da lista Varargs e os usa como o valor a ser impresso. Desde que você deu um formato assinado, ele o imprime e 0xffffffff é a representação do complemento dos dois de -1.

Você pode (deve) ver por que no aviso do compilador. Caso contrário, tente definir o nível de alerta mais alto. Com o VS, tenho este aviso: aviso C4245: 'Inicializando': conversão de '__int64' para '__int64 não assinada', incompatibilidade assinada/não assinada.

Não, não há transbordamento. É porque não está imprimindo todo o valor:

18446744073709551615 é o mesmo que 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff. Quando printf %d Processos que, ele pega apenas 32 bits (ou 64 bits se for uma CPU de 64 bits) para conversão, e esses são o valor assinado -1.

Se o printf Conversão foi %u Em vez disso, mostraria 4294967295 (32 bits) ou 18446744073709551615 (64 bits).

Um estouro é quando um valor aumenta até o ponto em que não se encaixa no armazenamento alocado. Nesse caso, o valor é alocado Muito bem, mas não está sendo completamente recuperado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top