Programmazione

Overflow E Conversione Di Tipo

Laboratorio Avanzato sul Linguaggio C: scopri cosa succede quando si assegna un valore unsigned int con suffisso U a una variabile int con segno. Analisi del Complemento a Due e dell'Integer Overflow.

Durata

20 minuti

Software

Compiler C / GCC / Dev-C++ / VS Code

Linguaggio

Linguaggio C

Laboratorio: Overflow e Conversione tra Unsigned e Signed

Argomenti: suffisso 'U', limits.h, INT_MAX, conversione implicita, complemento a due, bit di segno MSB, integer overflow

Cosa succede esattamente quando proviamo ad assegnare una costante unsigned int (indicata dal suffisso U) ad una variabile int con segno che non ha spazio sufficiente per contenerla? In questo laboratorio esploreremo il meccanismo interno della memoria, il fenomeno dell'Integer Overflow ed la rappresentazione binaria in Complemento a Due.

1. Il Limite di int e la Costante "Superiore"

Limiti Fisici: La soglia limite dei 32 bit con segno

Sui sistemi informatici moderni standard a 32 bit, un tipo int con segno occupa 4 Byte (32 bit) ed il suo range varia da $-2.147.483.648$ a $+2.147.483.647$ (valore rappresentato dalla costante INT_MAX della libreria <limits.h>).

Valore Massimo Signed (INT_MAX)

INT_MAX = 2.147.483.647 (231 - 1)

Se proviamo a memorizzare un valore superiore a questa cifra dentro una variabile int standard, lo spazio destinato ai numeri positivi si esaurisce.

Valore Unsigned (Suffisso U)

2147483648U (231)

Aggiungendo la U, il C gestisce il valore come unsigned int (range da $0$ a $+4.294.967.295$). Il numero $2.147.483.648U$ è quindi perfettamente valido per unsigned, ma di un'unità superiore a INT_MAX.

2. Laboratorio C: Assegnazione Pericolosa

Sorgente di Prova: Verificare l'effetto dell'assegnamento non coerente

Codice Sorgente C (overflow_lab.c):
1 #include <stdio.h>
2 #include <limits.h> // Per accedere alla costante INT_MAX
3
4 int main(void) {
5     int limiteMax = INT_MAX;
6     unsigned int valoreUnsigned = 2147483648U; // Valore > INT_MAX con suffisso 'U'
7     int risultatoErrato;
8
9     printf("INT_MAX (massimo positivo): %d\n", limiteMax);
10     printf("Costante unsigned: %u\n\n", valoreUnsigned);
11     // PERICOLO: Assegnazione da unsigned a int (overflow)
12     risultatoErrato = valoreUnsigned;
13
14     printf("Risultato inatteso (signed): %d\n", risultatoErrato);
15
16     return 0;
17 }
Output sul Terminale:
INT_MAX (massimo positivo): 2147483647
Costante unsigned:          2147483648

Risultato inatteso (signed): -2147483648

Risultato Sorprendente: Un numero positivo enorme (2.147.483.648) si è trasformato improvvisamente nel valore negativo più piccolo possibile (-2.147.483.648)!

3. Analisi Binaria: Cosa succede in Memoria RAM?

Sotto il Cofano: Il bit di segno (MSB) ed il Complemento a Due

Per comprendere la ragione di questo comportamento apparentemente bizzarro, dobbiamo osservare come il numero decimale 2.147.483.648 viene scritto in formato binario a 32 bit (in quanto equivale esattamente a $2^{31}$):

Mappatura del Registro a 32 bit:

Bit N° 31 (MSB) 30 29 ... 2 1 0
Valore Posizionale 231 230 229 ... 22 21 20
Stato del Bit 1 0 0 ... 0 0 0

Il Bit Most Significant (MSB)

Nei sistemi C che adottano la rappresentazione standard in Complemento a Due, il primo bit a sinistra (Bit 31, chiamato Most Significant Bit) ha il compito di indicare il segno del numero:
• Bit 31 = 0 ➔ Il numero è Positivo.
• Bit 31 = 1 ➔ Il numero è Negativo.

Cosa accade durante l'assegnazione?

Quando il valore binario 10000000 00000000 00000000 00000000 viene forzato dentro una variabile int (signed), il compilatore interpreta il bit 31 pari ad 1 non più come $+2^{31}$, ma come $-2^{31}$, producendo l'output -2.147.483.648.

4. Buone Pratiche: Come evitare questo errore?

Prevenzione: Scelta corretta dei tipi e dei suffissi

1. Mantieni Coerenza tra Tipo e Variabile

Se usi il suffisso U per definire una costante senza segno, assicurati che anche la variabile di destinazione sia dichiarata come unsigned int.

2. Usa Tipi Capienti per Grandi Cifre (ULL)

Se hai bisogno di memorizzare cifre molto grandi senza rischiare l'overflow nei numeri con segno, utilizza variabili di tipo unsigned long long insieme al suffisso ULL.

Regola d'Oro per la Programmazione in C

Non fare mai affidamento su conversioni implicite tra tipi unsigned e tipi signed quando le costanti superano INT_MAX. Il tipo della variabile di destinazione è il fattore limitante finale che determina l'interpretazione del valore in memoria!


In sintesi

Argomenti: concetti principali, riepilogo, competenze

  1. I valori scritti con suffisso U (es. 2147483648U) nascono come unsigned int e possono superare INT_MAX.
  2. Assegnare un valore unsigned maggiore di INT_MAX a una variabile int con segno genera una conversione implicita pericolosa.
  3. A livello binario, il bit più significativo (MSB) impostato a 1 viene interpretato come il segno negativo nel sistema di rappresentazione in Complemento a Due.
  4. Per prevenire risultati errati o imprevedibili è essenziale garantire coerenza tra il tipo del literal numerico e il tipo della variabile di destinazione.

Cosa hai imparato
  1. Identificare il limite INT_MAX (2.147.483.647 su sistemi a 32 bit).
  2. Analizzare l'impatto del bit di segno (MSB) nel Complemento a Due.
  3. Spiegare perché 2147483648U assegnato a un int produce il valore -2147483648.
  4. Applicare i suffissi U ed ULL per la gestione sicura di costanti di grandi dimensioni.


Linguaggio C, Integer Overflow, Suffisso U, Complemento a Due, unsigned int, limits.h, INT_MAX,