Corso C avanzato su Raspberry PI: Tipi di variabili

Questo argomento è stato affrontato abbastanza superficialmente durante il corso base del linguaggio C con il Raspberry Pi, proprio per dare modo, ai principianti, di comprenderne la filosofia principale. In questa lezione approfondiremo, ancora di più, i concetti fondamentali sulle variabili e sul loro utilizzo. Vedremo, anche, il loro comportamento in termini di occupazione di spazio nella memoria RAM e le tecniche per utilizzarle al meglio. La presenza di abbondante codice dovrebbe favorire proprio il processo di acquisizione dei vari concetti.

Introduzione

Ricordiamo, ancora una volta, che una variabile è un nome attribuito a una piccola area di memoria, nella quale si può allocare un valore che il programma può manipolare. Nel linguaggio C esistono diversi tipi di variabili, differenti tra loro per la capienza, per l'intervallo di valori memorizzabili e per la varietà dei dati da immagazzinare. Il nome di una variabile può essere composto da lettere, numeri e dal carattere di "underscore". Esso non può iniziare per un numero e c'è differenza tra i caratteri maiuscoli e minuscoli. Vediamo qualche esempio:

  • int k; (corretto);
  • int cognome_e_nome; (corretto);
  • int Cognome-Nome (sbagliato);
  • int COGNOME_e_Nome; (corretto);
  • int area12; (corretto);
  • int 12area; (sbagliato).

Vari tipi di variabili

Esploreremo, adesso, le varie tipologie di variabili che, vedremo, sono molte più numerose di quelle esaminate durante i precedenti corsi sul linguaggio C erogati in queste pagine. Utilizzeremo, altresì, la funzione sizeof() allo scopo di determinare lo spazio occupato in memoria. La seguente tabella fornisce una panoramica generale sulle tipologie di variabili, il loro contenuto e la relativa capienza e può risultare utile per una rapida consultazione.

Tipo variabile Occupazione bytes e bits
Intervallo unsigned Inervallo signed
char 1 (8) da 0 a 255 da -128 a +127
short int 2 (16) da 0 a 65535 da - 32768 a +32767
int 4 (32) da 0 a 4294967295 da -2147483648 a +2147483647
long int  4 (32) da 0 a 4294967295 da -2147483648 a +2147483647
long long int 8 (64) da 0 a 18446744073709551615 da -9223372036854775808 a +9223372036854775807
float 4 (32) +/- 3.4e +/- 38 (circa 7 digits)
double 8 (64) +/- 1.7e +/- 308 (circa 15 digits)
long double 16 (128) +/- 1.7e +/- 308 (~15 digits)

Variabili di tipo char

Una variabile di tipo char rappresenta un dato di tipo intero (ossia senza cifre decimali), che occupa un solo byte (8 bit) in memoria RAM. La seguente codifica testimonia tale fatto.

#include <stdio.h>
int main() {
    char valore;
    printf("Spazio occupato in bytes: %d\n",sizeof(valore));
    return 0;
}

che produce il seguente risultato:

Spazio occupato in bytes: 1

E' possibile anteporre il modificatore signed o unsigned in modo da poter gestire i seguenti intervalli di valore:

  • unsigned char: da 0 a 255 (ossia da 00000000b a 11111111b);
  • signed char: da -128 a +127 (ossia da 10000000b a 01111111b).

Pertanto, un errore di scelta di dominio ne potrebbe produrre un altro in visualizzazione, sebbene il tipo di variabile sia corretto. Ad esempio, il seguente codice:

#include <stdio.h>
int main() {
    unsigned char valore;
    valore = -5;
    printf("Contenuto della variabile: %d. Spazio occupato in bytes: %d\n",valore,sizeof(valore));
    return 0;
}

produce, in output il risultato:

Contenuto della variabile: 251. Spazio occupato in bytes: 1

sebbene il valore -5 sia stato giustamente assegnato alla variabile. Per ottenere il corretto risultato occorre utilizzare il segnaposto "signed". Il programmatore deve, dunque, essere molto sensibile alla scelta oculata dei tipi di variabili ed è buona norma prevedere, in largo anticipo, il futuro contenuto.

Un'assegnazione di un valore decimale alla variabile di tipo char non produce alcun errore in fase di compilazione, ma i calcoli e le visualizzazioni risultano, ovviamente, troncate e limitate solo alla parte intera. Ad esempio, nel seguente codice:

#include <stdio.h>
int main() {
    unsigned char valore;
    valore = 44.7;
    valore = valore * 2 + 1;
    printf("Contenuto della variabile: %d. Spazio occupato in bytes: %d\n",valore,sizeof(valore));
    return 0;
}

si otterrà, in uscita, il seguente messaggio:

Contenuto della variabile: 89. Spazio occupato in bytes: 1

anziché il corretto risultato matematico di 90.4.

Anche il verificarsi di possibili overflow possono mettere in crisi i programmatori più smaliziati. Il seguente codice raddoppia il valore di una variabile definita, volutamente per l'esempio, di tipo char:

#include <stdio.h>
int main() {
    unsigned char valore;
    valore = 200 * 2;
    printf("Contenuto della variabile: %d. Spazio occupato in bytes: %d\n",valore,sizeof(valore));
    return 0;
}

Il risultato aspettato (e corretto) sarebbe 400, ma dal momento che le informazioni "traboccano" dalla variabile, il risultato visualizzato è il seguente:

Contenuto della variabile: 144. Spazio occupato in bytes: 1

Per fortuna, la maggior parte delle volte, il compilatore controlla molto bene il codice per cui, in questo caso, il programmatore riceverebbe un messaggio di warning di cui in figura 1, che lo metterà subito in guardia del pericolo.

Figura 1: grazie ai messaggi di warning è possibile comprendere l'errore

Figura 1: grazie ai messaggi di warning è possibile comprendere l'errore

Purtroppo, il compilatore non è sempre in grado di controllare eventuali condizioni di overflow. Esso, infatti, non esegue preliminarmente il codice ma si limita a un'analisi delle sintassi e delle operazioni matematiche esplicite. Nel successivo listato la compilazione avviene correttamente, senza alcun avvertimento da parte del compilatore, ma in esecuzione avviene il traboccamento della variabile al quarto incremento, secondo la sequenza dei risultati 80, 160, 240, 64 (e non, come si ci potrebbe aspettare, 320).

#include <stdio.h>
int main() {
    unsigned char valore;
    valore = 0;
    valore = valore + 80;
    valore = valore + 80;
    valore = valore + 80;
    valore = valore + 80;
    printf("Contenuto della variabile: %d. Spazio occupato in bytes: %d\n",valore,sizeof(valore));
    return 0;
}

L'output finale, infatti è il seguente:

Contenuto della variabile: 64. Spazio occupato in bytes: 1

Il seguente altro codice è estremamente curioso e interessante. Esso focalizza il fatto di come un determinato valore possa essere espresso in diverse modalità, nell'esempio rispettivamente di tipo decimale, esadecimale, binario e carattere.

#include <stdio.h>
   int main() {
   unsigned char v1,v2,v3,v4;
   v1 = 65;
   v2 = 0x41;
   v3 = 0b1000001;
   v4 = 'A';   // Singoli apici
   printf("v1=%d, v2=%d, v3=%d, v4=%d\n",v1,v2,v3,v4);
   return 0;
 }

Il risultato della sua esecuzione è la seguente:

v1=65, v2=65, v3=65, v4=65

Variabili di tipo int

Rappresentano delle variabili intere la cui dimensione è quella più naturale per il processore ospitante, in termini di occupazione di memoria nei registri della CPU. La loro capienza potrebbe variare da CPU a CPU. Potrebbero occupare 2, 4 oppure 8 bytes. Esse dipendono dalla macchina su cui si sta lavorando e compilando. Il seguente listato dichiara una variabile di tipo int, le assegna un valore e, infine, visualizza il numero di byte che essa occupa in RAM, assieme al suo valore.

#include <stdio.h>
int main() {
    int valore;
    valore = 1234;
    printf("Contenuto della variabile: %d. Spazio occupato in bytes: %d\n",valore,sizeof(valore));
    return 0;
}

Il risultato mostrato a video è il seguente:

Contenuto della variabile: 1234. Spazio occupato in bytes: 4
[...]

ATTENZIONE: quello che hai appena letto è solo un estratto, l'Articolo Tecnico completo è composto da ben 2389 parole ed è riservato agli ABBONATI. Con l'Abbonamento avrai anche accesso a tutti gli altri Articoli Tecnici e potrai fare il download in formato PDF eBook e Mobi per un anno. ABBONATI ORA, è semplice e sicuro.

Una risposta

  1. Giovanni Di Maria Giovanni Di Maria 2 aprile 2019

Scrivi un commento

Send this to a friend