Finora, in questa serie di tutorial sulla programmazione C in corso, abbiamo discusso alcuni concetti, ma ne abbiamo perso uno di base. Si tratta di numeri negativi. Sì, sebbene in uno dei nostri tutorial iniziali abbiamo menzionato brevemente le variabili con segno e senza segno, in realtà non abbiamo discusso di come i numeri negativi vengono archiviati in memoria.
Bene, questo è esattamente ciò che verrà discusso in questo tutorial. Quindi, senza ulteriori indugi, iniziamo con la discussione.
complemento a 2
Prima di iniziare con la spiegazione sulla rappresentazione dei numeri negativi in memoria, è importante conoscere il concetto di complemento a 1 e 2, entrambi operazioni a livello binario.
Facciamo un esempio molto semplice. Supponiamo di avere un numero intero di 4 byte 'a' con valore decimale 15. Quindi ecco come viene rappresentato nella memoria in forma binaria:
00000000 00000000 00000000 00001111
Ora, per calcolare il complemento a uno, basta invertire tutti i bit. Quindi la seguente è la rappresentazione in complemento a 1 di 15:
11111111 11111111 11111111 11110000
Ora, se aggiungi 1 alla rappresentazione binaria sopra, ottieni il complemento a 2.
11111111 11111111 11111111 11110001
Quindi la rappresentazione sopra è il complemento a due di 15.
Numeri negativi
Ora, alcuni di voi staranno pensando perché abbiamo discusso del complemento di 1 e 2? Ebbene, la risposta sta nel fatto che la rappresentazione binaria di un numero negativo viene calcolata tramite il complemento a 2.
Difficile da credere? Ecco la prova:
Il complemento a 2 che abbiamo calcolato nella sezione precedente può essere rappresentato in forma esadecimale come 0xFFFFFFFF1. Ora, vediamo qual è questo valore in forma decimale tramite un programma C
Ecco il codice:
#include <stdio.h>
int main()
{
int a = 0xFFFFFFF1;
printf("a = %d", a);
return 0;
}
E il seguente è l'output:
a = -15
Ci credi adesso? Abbiamo iniziato con un numero '15', calcolato il suo complemento a 2 e quando abbiamo convertito nuovamente il valore del complemento a due in decimale, abbiamo scoperto che è -15.
Andando avanti, ora modifichiamo leggermente il codice per assicurarci che la chiamata printf legga il valore della variabile 'a'
#include <stdio.h>
int main()
{
int a = 0xFFFFFFF1;
printf("a = %u", a);
return 0;
}
Ecco l'output ora:
a = 4294967281
Oops, l'output è cambiato e ora è un enorme valore positivo. Ma perché è successo? 0xFFFFFFFF1 non è il complemento a 2 di 15 come abbiamo visto prima?
Sì, 0xFFFFFFFF1 è un complemento a due di 15, ma se non lo guardi da quella prospettiva, è anche un valore normale (4294967281). La differenza sta nel modo in cui viene letto. Se viene letto come intero con segno (tramite %d in printf), vedrai l'output come -15, ma se viene letto come intero senza segno (tramite %u in printf), vedrai l'output come 4294967281.
Come regola pratica con le variabili con segno (che trattano sia valori negativi che positivi), tieni presente che la rappresentazione binaria di numeri negativi ha sempre '1' come bit più a sinistra, mentre in caso di numeri positivi il bit in questione è sempre 0.
Infine, nota che puoi anche invertire una rappresentazione in complemento a due per ottenere la sua controparte positiva. Ad esempio, prendiamo di nuovo il valore 0xFFFFFFF1, che è la rappresentazione esadecimale di -15. È rappresentato in forma binaria come:
11111111 11111111 11111111 11110001
Ora, per ottenere la sua controparte positiva, esegui di nuovo un complemento a 2. Ciò significa che prima fai un complemento a 1:
00000000 00000000 00000000 00001110
E poi aggiungi 1
00000000 00000000 00000000 00001111
Ora, se lo converti, otterrai il valore 15 in forma decimale.
Conclusione
Spero che questo tutorial ti abbia aiutato a capire il concetto di numeri negativi nel contesto di come sono rappresentati nella memoria. Ti suggerisco di provare gli esempi che abbiamo utilizzato in questo tutorial e, in caso di problemi, dubbi o domande, lasciaci un commento qui sotto.