La Variabile Globale errno in Linguaggio C
errnoè una variabile globale di tipointdefinita nell'header<errno.h>, utilizzata per segnalare errori nelle funzioni di libreria standard C.- Quando una funzione di libreria standard C incontra un errore, imposta
errnocon un codice di errore specifico, che può essere utilizzato per determinare la natura dell'errore. - È buona pratica resettare
errnoa0prima di chiamare una funzione che potrebbe fallire, in modo da poter distinguere tra un errore effettivo e il valore precedente dierrno. - Alcuni dei valori standard di
errnoincludonoEDOM(errore di dominio matematico) eERANGE(risultato fuori dall'intervallo rappresentabile). - La gestione degli errori tramite
errnoconsente di scrivere codice più robusto e resiliente in linguaggio C.
La variabile globale errno
Nella lezione precedente abbiamo visto come gestire gli errori in linguaggio C utilizzando i valori di ritorno delle funzioni. In questa lezione esploreremo un'altra tecnica comune per la gestione degli errori in C, che si basa sull'uso della variabile globale errno.
L'uso di errno discende dalla convenzione adoperata nel sistema operativo UNIX (e anche linux) dove, ogniqualvolta si verifica un errore in una chiamata di sistema, viene impostata una variabile globale chiamata errno con un codice di errore specifico. Questo codice può poi essere utilizzato dal programma per determinare la natura dell'errore e agire di conseguenza.
Qui ci limiteremo a spiegare come funziona errno in linguaggio C e quali valori può assumere secondo lo standard del linguaggio C, tralasciando, invece, le specifiche implementazioni del sistema operativo.
In generale, errno è una variabile globale di tipo int definita nell'header <errno.h> (Anche se può anche essere definita come macro, ma questo è un dettaglio di implementazione). Alcune delle funzioni di libreria standard C, come tutte le funzioni matematiche, quando incontrano un errore, impostano errno con un valore che rappresenta il tipo di errore verificatosi. In altre parole, l'errore non viene segnalato attraverso il valore di ritorno della funzione, ma piuttosto attraverso la variabile globale errno.
Per cui, lo schema generale per l'uso di errno è il seguente:
#include <errno.h>
/* ... */
/* 1. Si resetta errno a 0 prima della chiamata alla funzione */
errno = 0;
/* 2. Si chiama la funzione che può fallire */
valore = funzione_che_puo_fallire(parametri);
/* 3. Si controlla se errno è stato modificato */
if (errno != 0) {
// Gestisci l'errore in base al valore di errno
} else {
// Procedi con l'elaborazione normale
}
Notiamo due dettagli importanti in questo schema:
-
Non c'è bisogno di dichiarare
errno, poiché è già definita nell'header<errno.h>.Anzi, è un errore ridichiararla nel proprio codice, in quanto potrebbe oscurare la definizione originale e causare comportamenti imprevisti.
-
La variabile
errnoviene modificata solo quando si verifica un errore.Se la funzione chiamata ha successo,
errnorimane invariato. Per questo motivo, è buona pratica resettareerrnoa0prima di chiamare una funzione che potrebbe fallire, in modo da poter distinguere tra un errore effettivo e il valore precedente dierrno.
Ad esempio, se dobbiamo chiamare due funzioni che possono fallire, dobbiamo resettare errno prima di ciascuna chiamata. In caso contrario, potremmo confondere un errore della prima funzione con un errore della seconda:
#include <errno.h>
/* ... */
errno = 0;
valore1 = funzione_che_puo_fallire1(parametri1);
if (errno != 0) {
// Gestisci l'errore di funzione_che_puo_fallire1
}
errno = 0;
valore2 = funzione_che_puo_fallire2(parametri2);
if (errno != 0) {
// Gestisci l'errore di funzione_che_puo_fallire2
}
Nessuna funzione di libreria standard C imposta errno a 0
È importante notare che nessuna funzione della libreria standard C imposta errno a 0 in caso di successo. Pertanto, è responsabilità del programmatore resettare errno a 0 prima di chiamare una funzione che potrebbe fallire, se si intende utilizzare errno per rilevare errori.
Esempio: Radice quadrata
Vediamo un esempio pratico di utilizzo di errno con la funzione sqrt della libreria matematica standard C, che calcola la radice quadrata di un numero.
La funzione sqrt restituisce un valore di tipo double, ma se viene chiamata con un numero negativo, non può calcolare la radice quadrata reale e imposta errno a EDOM, che indica un errore di dominio matematico.
Quindi, possiamo scrivere un programma che utilizza sqrt e gestisce l'errore tramite errno:
#include <stdio.h>
#include <math.h>
#include <errno.h>
int main() {
double numero;
printf("Inserisci un numero per calcolare la sua radice quadrata: ");
scanf("%lf", &numero);
errno = 0; // Resetta errno prima della chiamata
double risultato = sqrt(numero);
if (errno == EDOM) {
printf("Errore: Impossibile calcolare la "
"radice quadrata di un numero negativo.\n");
} else {
printf("La radice quadrata di %.2f è %.2f\n", numero, risultato);
}
return 0;
}
Si noti che per compilare questo programma, potrebbe essere necessario collegare la libreria matematica utilizzando l'opzione -lm con il compilatore GCC:
gcc -o esempio_sqrt esempio_sqrt.c -lm
Nell'esempio sopra, se l'utente inserisce un numero negativo, sqrt imposterà errno a EDOM, e il programma stamperà un messaggio di errore appropriato. Altrimenti, stamperà il risultato della radice quadrata.
Valori di errno
I valori che la variabile globale errno può assumere sono definiti nell'header <errno.h>.
In generale, il sistema operativo UNIX (e Linux) e lo standard POSIX definiscono diversi codici di errore comuni che vengono impostati in errno. Lo standard C, tuttavia, ne definisce solo alcuni, lasciando agli ambienti specifici l'implementazione di ulteriori codici di errore.
I valori standard di errno definiti nello standard C sono delle macro costanti, tra cui:
EDOM: Viene impostato quando un'operazione matematica riceve un argomento fuori dal dominio valido (ad esempio, calcolare la radice quadrata di un numero negativo).ERANGE: Viene impostato quando il risultato di un'operazione matematica è fuori dall'intervallo rappresentabile (ad esempio, un overflow). Ad esempio, se passiamo il valore1000alla funzioneexp, che calcola l'esponenziale, il risultato sarà troppo grande per essere rappresentato come undouble, in quantoè un numero estremamente grande non rappresentabile da una variabile di tipo double. In questo caso,errnoverrà impostato aERANGE.
Molte funzioni matematiche potrebbero incontrare entrambe gli errori di sopra. Quindi, è buona pratica controllare entrambi i valori di errno dopo aver chiamato una funzione matematica.