Perché il più piccolo int, −2147483648, ha il tipo 'long'?

Perché il più piccolo int, −2147483648, ha il tipo 'long'?

In C, -2147483648 non è una costante intera. 2147483648 è una costante intera e - è solo un operatore unario applicato ad esso, producendo un'espressione costante. Il valore di 2147483648 non rientra in un int (è troppo grande, 2147483647 è in genere il numero intero più grande) e quindi la costante intera ha tipo long , che causa il problema che osservi. Se vuoi menzionare il limite inferiore per un int , usa la macro INT_MIN da <limits.h> (l'approccio portatile) o evita accuratamente di menzionare 2147483648 :

printf("PRINTF(d) \t: %d\n", -1 - 2147483647);

Il problema è che -2147483648 non è un intero letterale. È un'espressione composta dall'operatore di negazione unario - e il numero intero 2147483648 , che è troppo grande per essere un int se int s sono 32 bit. Poiché il compilatore sceglierà un intero con segno di dimensioni appropriate per rappresentare 2147483648 prima di applicare l'operatore di negazione, il tipo del risultato sarà maggiore di un int .

Se sai che il tuo int s sono a 32 bit e si desidera evitare l'avviso senza compromettere la leggibilità, utilizzare un cast esplicito:

printf("PRINTF(d) \t: %d\n", (int)(-2147483648));

Questo è il comportamento definito su una macchina in complemento a 2 con int a 32 bit s.

Per una maggiore portabilità teorica, usa INT_MIN invece del numero e facci sapere dove hai trovato una macchina non complementare a 2 su cui testarla.

Per essere chiari, quell'ultimo paragrafo era in parte uno scherzo. INT_MIN è sicuramente la strada da percorrere se intendi "il più piccolo int ", perché int varia nelle dimensioni. Ci sono ancora molte implementazioni a 16 bit, ad esempio. Scrivendo -2 31 è utile solo se intendi sempre esattamente quel valore, nel qual caso probabilmente useresti un tipo di dimensioni fisse come int32_t invece di int .

Potresti volere qualche alternativa per scrivere il numero in decimale per renderlo più chiaro per coloro che potrebbero non notare la differenza tra 2147483648 e 2174483648 , ma devi stare attento.

Come accennato in precedenza, su una macchina complementare a 2 bit a 32 bit, (int)(-2147483648) non traboccherà ed è quindi ben definito, perché -2147483648 sarà trattato come un tipo firmato più ampio. Tuttavia, lo stesso non vale per (int)(-0x80000000) . 0x80000000 verrà trattato come un unsigned int (poiché rientra nella rappresentazione non firmata); -0x80000000 è ben definito (ma il - non ha effetto se int è 32 bit) e la conversione del unsigned int risultante 0x80000000 a int comporta un overflow. Per evitare l'overflow, dovresti eseguire il cast della costante esadecimale su un tipo con segno:(int)(-(long long)(0x80000000)) .

Allo stesso modo, è necessario prestare attenzione se si desidera utilizzare l'operatore del turno di sinistra. 1<<31 è un comportamento indefinito su macchine a 32 bit con int a 32 bit (o inferiore) S; valuterà solo 2 31 se int è almeno 33 bit, perché lo spostamento a sinistra di k bit è ben definito solo se k è rigorosamente inferiore al numero di bit senza segno del tipo intero dell'argomento di sinistra.

1LL<<31 è sicuro, poiché long long int è richiesto di poter rappresentare 2 63 -1, quindi la sua dimensione in bit deve essere maggiore di 32. Quindi il modulo

(int)(-(1LL<<31))

è forse il più leggibile. YMMV.

Per qualsiasi pedante di passaggio, questa domanda è contrassegnata con C e l'ultima bozza C (n1570.pdf) dice, rispetto a E1 << E2 , dove E1 ha un tipo firmato, che il valore è definito solo se E1 è non negativo e E1 × 2E2 "è rappresentabile nel tipo di risultato". (§6.5.7 comma 4).

È diverso dal C++, in cui l'applicazione dell'operatore di spostamento a sinistra è definita se E1 è non negativo e E1 × 2E2 "è rappresentabilenel corrispondente tipo non firmato del tipo di risultato" (§5.8 cpv. 2, corsivo aggiunto).

In C++, secondo la bozza di standard più recente, la conversione di un valore intero in un tipo intero con segno è definita dall'implementazione se il valore non può essere rappresentato nel tipo di destinazione (§4.7 comma 3). Il corrispondente paragrafo della norma C -- §6.3.1.3 par. 3 -- dice che "o il risultato è definito dall'implementazione o viene generato un segnale definito dall'implementazione".)