Determinare le Caratteristiche degli Interi in Linguaggio C
Secondo lo standard del linguaggio C, le caratteristiche dei tipi di dati interi, in particolar modo le loro dimensioni e gli intervalli di valori rappresentabili, possono variare a seconda dell'implementazione del compilatore e della piattaforma in uso.
Per garantire la portabilità del codice e per scrivere programmi robusti, è importante essere in grado di determinare queste caratteristiche in modo programmatico. In questa lezione esploreremo come ottenere queste informazioni utilizzando l'header <limits.h> della libreria standard C.
- Le caratteristiche dei tipi di dati interi in C, come dimensioni e intervalli di valori, possono variare tra diverse implementazioni del compilatore.
- L'header
<limits.h>fornisce una serie di macro che definiscono i limiti minimi e massimi dei tipi di dati interi supportati. - Le macro definite in
<limits.h>includono limiti per tipi comechar,short,int,long, elong long, sia con segno che senza segno. - Utilizzare queste macro consente di scrivere codice portabile e robusto, adattandosi alle caratteristiche specifiche della piattaforma di esecuzione.
L'header <limits.h>
La libreria standard C fornisce l'header <limits.h>, che definisce una serie di macro che rappresentano i limiti minimi e massimi dei tipi di dati interi supportati dal compilatore e dalla piattaforma in uso. Queste macro sono utili per scrivere codice portabile che deve adattarsi alle caratteristiche specifiche dell'ambiente di esecuzione.
Questo header non definisce alcuna funzione, ma solo macro costanti. Possiamo dividere l'insieme di macro definite in due categorie principali:
- Macro per i tipi
chare varianti (signed char,unsigned char). - Macro per i tipi interi standard (
short,int,long,long longe le loro varianti senza segno).
Partiamo dal primo gruppo. Le macro relative ai tipi char sono mostrate nella tabella seguente:
| Macro | Valore | Descrizione |
|---|---|---|
CHAR_BIT |
Numero di bit per un byte | |
SCHAR_MIN |
Valore minimo per signed char |
|
SCHAR_MAX |
Valore massimo per signed char |
|
UCHAR_MAX |
Valore massimo per unsigned char |
|
CHAR_MIN |
Varia | Valore minimo per char. Questo valore dipende dal fatto che char sia considerato con segno o senza segno. Il valore minimo sarà uguale a SCHAR_MIN o 0 rispettivamente. |
CHAR_MAX |
Varia | Valore massimo per char. Questo valore dipende dal fatto che char sia considerato con segno o senza segno. Il valore massimo sarà uguale a SCHAR_MAX o UCHAR_MAX rispettivamente. |
MB_LEN_MAX |
Numero massimo di byte necessari per rappresentare un carattere multibyte (studieremo i caratteri multibyte in un capitolo successivo). |
Passiamo ora alle macro relative ai tipi interi standard. La tabella seguente mostra queste macro:
| Macro | Valore | Formula | Descrizione |
|---|---|---|---|
SHRT_MIN |
Valore minimo per short |
||
SHRT_MAX |
Valore massimo per short |
||
USHRT_MAX |
Valore massimo per unsigned short |
||
INT_MIN |
Valore minimo per int |
||
INT_MAX |
Valore massimo per int |
||
UINT_MAX |
Valore massimo per unsigned int |
||
LONG_MIN |
Valore minimo per long |
||
LONG_MAX |
Valore massimo per long |
||
ULONG_MAX |
Valore massimo per unsigned long |
||
LLONG_MIN |
Valore minimo per long long |
||
LLONG_MAX |
Valore massimo per long long |
||
ULLONG_MAX |
Valore massimo per unsigned long long |
Le macro definite all'interno dell'header <limits.h> sono estremamente utili per scrivere codice portabile e robusto, poiché permettono di adattarsi alle caratteristiche specifiche dell'ambiente di esecuzione senza dover fare assunzioni rigide sui limiti dei tipi di dati. Per esempio, possiamo utilizzare queste macro per verificare se un intero int sia in grado di memorizzare un certo valore prima di assegnarlo, evitando così overflow o comportamenti indefiniti.
Ecco un esempio di utilizzo delle macro di <limits.h>:
#include <limits.h>
#if INT_MAX < 100000
#error "Il tipo int non può rappresentare il valore 100000"
#endif
In questo esempio, utilizziamo la macro INT_MAX per verificare se il tipo int può rappresentare il valore 100000. Se INT_MAX è inferiore a 100000, viene generato un errore di compilazione e il programma non viene compilato.
Le macro possono essere addirittura utilizzate per creare codice condizionale che si adatta alle caratteristiche della piattaforma. Infatti, potremmo usare la compilazione condizionale per scegliere quale tipo adoperare in base ai limiti definiti in <limits.h>.
Ad esempio, supponiamo di dover scrivere un programma che memorizza dei numeri interi al massimo grandi quanto 1000000, ossia un milione. Tali numeri devono essere memorizzati in una variabile di tipo intero, che si chiama quantita. Tuttavia, non sappiamo a priori se il tipo int sia in grado di memorizzare un numero così grande, poiché il suo intervallo dipende dalla piattaforma.
Se la piattaforma supporta un tipo int con un intervallo sufficiente, possiamo usare int. Altrimenti, dobbiamo usare un tipo più grande, come long o long long.
A questo scopo, possiamo utilizzare le macro di <limits.h> per selezionare il tipo più adatto in fase di compilazione:
#include <limits.h>
#if INT_MAX >= 1000000
typedef int quantita_t;
#elif LONG_MAX >= 1000000
typedef long quantita_t;
#elif LLONG_MAX >= 1000000
typedef long long quantita_t;
#else
#error "Nessun tipo intero disponibile può rappresentare il valore 1000000"
#endif
/* ... */
quantita_t quantita;
In questo esempio, utilizziamo la compilazione condizionale per definire un nuovo tipo quantita_t, utilizzando la dichiarazione typedef. Dopo aver verificato i limiti di int, long e long long, scegliamo il tipo più piccolo che può rappresentare il valore 1000000. Se nessuno dei tipi disponibili può rappresentare tale valore, viene generato un errore di compilazione.
Successivamente, possiamo utilizzare il tipo quantita_t per dichiarare la variabile quantita, che sarà del tipo più adatto in base alla piattaforma di compilazione.