Menu

Argomenti da riga di comando in C: argc, argv e parsing sicuro

Come un programma C legge la sua riga di comando: com'è fatto argc e argv, come scorrere gli argomenti, cosa contiene argv[0], come convertire numeri con strtol invece di atoi e una piccola calcolatrice che mette tutto insieme.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

A ogni programma che esegui da un terminale si possono passare argomenti: gcc -Wall hello.c -o hello ne passa quattro. I tuoi programmi C leggono i propri tramite due parametri di main, e il meccanismo è tutto qui: nessuna chiamata di libreria, nessuna configurazione.

La firma

Finora ogni esempio di questa documentazione ha usato int main(void). L'altra forma standard riceve la riga di comando:

int main(int argc, char *argv[]) {
    /* ... */
}
  • argc ("argument count") è il numero di argomenti, compreso il nome del programma.
  • argv ("argument vector") è un array di stringhe. char *argv[] significa "array di puntatori a char", cioè un array di stringhe C.

char **argv è una scrittura equivalente che vedrai altrettanto spesso; i parametri array decadono a puntatori, quindi le due dichiarano la stessa cosa.

Usa il pannello Args dell'editor per fornire gli argomenti (ogni campo è un argomento), poi premi Run. Prova con tre campi: hello, world, 42.

Com'è fatto

Eseguire ./greet Ada Lovelace produce:

IndiceValore
argv[0]"./greet"il nome del programma come è stato invocato
argv[1]"Ada"primo argomento vero
argv[2]"Lovelace"secondo argomento vero
argv[3]NULLil terminatore
argc3conteggio che include argv[0]

Quattro fatti da memorizzare:

  • argc vale almeno 1 in pratica, perché argv[0] è il nome del programma. Gli argomenti veri partono dall'indice 1, ed è per questo che i cicli su di essi partono da i = 1.
  • Tutto è una stringa. ./prog 42 ti dà "42", i due caratteri, mai il numero 42. Convertirlo spetta a te.
  • argv[argc] è NULL. Lo garantisce lo standard, quindi se preferisci puoi percorrere l'array senza argc: for (char **p = argv + 1; *p != NULL; p++).
  • È la shell a dividere le parole, non il tuo programma. ./prog "Ada Lovelace" è un solo argomento; le virgolette vengono consumate dalla shell e non arrivano mai in argv.

Controllare prima di leggere

Un argomento non passato non è una stringa vuota: è memoria che non hai alcun diritto di toccare. Valida sempre prima argc:

Due convenzioni in questo programmino, entrambe standard negli strumenti Unix:

  • Il messaggio di utilizzo va su stderr, non su stdout, così non sporca l'output che viene inviato altrove con una pipe.
  • Un valore di ritorno diverso da zero da main segnala un fallimento a chiunque abbia eseguito il programma. 0 significa successo; qualsiasi altro valore significa che qualcosa è andato storto. Gli script di shell lo controllano.

Stampare argv[0] invece di un nome scritto a mano fa sì che il messaggio corrisponda a come l'utente ha invocato il programma, anche se è stato rinominato o raggiunto da un percorso diverso.

Convertire gli argomenti numerici

argv[1] è testo. Per fare calcoli devi convertirlo, e la funzione da usare è strtol di <stdlib.h>:

Aggiungi 10, 20, 30 come tre campi Args, poi sostituiscine uno con banana per vedere il percorso di errore.

Il terzo parametro di strtol è la base: 10 per il decimale, 16 per l'esadecimale, oppure 0 per rilevarla automaticamente da un prefisso 0x o da uno zero iniziale. Il secondo indica dove salvare un puntatore al primo carattere non consumato, ed è questo che rende possibile il controllo degli errori:

  • end == text significa che non ha letto nulla: l'argomento non iniziava con un numero.
  • *end != '\0' significa che dopo il numero c'era altro testo, quindi "12abc" viene rifiutato invece di diventare 12 in silenzio.
  • errno == ERANGE significa che il valore ha superato i limiti di long.

Confronta con atoi, che è più breve e non può segnalare nulla di tutto ciò:

int n = atoi(argv[1]);   /* "banana" -> 0, "0" -> 0, overflow -> indefinito */

atoi restituisce 0 per un input non valido, indistinguibile da un vero zero. Usa strtol (e strtod per la virgola mobile) ogni volta che l'input arriva dall'esterno del programma. Approfondimenti in conversione di stringhe.

Una piccola calcolatrice

Mettiamo tutto insieme: un programma che riceve un numero, un operatore e un numero:

Prova 12 + 30, poi 7 x 6, poi 5 / 0.

Nota strcmp(op, "+") == 0 invece di op == "+". Confrontare valori char * con == confronta gli indirizzi, non il contenuto, e risulterebbe falso anche per testi identici. Vedi le funzioni per le stringhe.

La x al posto di * è un dettaglio del mondo reale: la maggior parte delle shell espande un * isolato nell'elenco dei file della directory corrente prima ancora che il tuo programma parta. Metterlo tra virgolette ('*') funziona, ma scegliere un carattere che la shell ignora è più comodo.

Parsing dei flag

Per convenzione le opzioni iniziano con -. Un semplice ciclo scritto a mano gestisce i casi comuni:

Provalo con quattro campi Args: -v, -n, 3, hello.

Il controllo i + 1 >= argc prima di argv[++i] è la riga importante: un'opzione che si aspetta un valore deve verificare che il valore ci sia davvero, altrimenti -n come ultimo argomento legge oltre la fine dell'array. Sui sistemi Unix reali getopt di <unistd.h> fa tutto questo per te, comprese le opzioni brevi raggruppate come -vn3; conviene usarlo quando un programma ha più di due o tre flag.

Argomenti nell'editor del browser

I blocchi editor di questa pagina vengono compilati ed eseguiti per te, e il pannello Args è dove fornisci quello che in un terminale seguirebbe il nome del programma. Ogni campo che aggiungi con Add arg diventa esattamente una voce in argv: in mezzo non c'è nessuna shell, quindi niente viene diviso sugli spazi e non servono virgolette: un campo che contiene hello world arriva come l'unico argomento hello world. Per passare -n 3 hello, aggiungi tre campi. Lasciare il pannello vuoto dà argc == 1: ecco perché il controllo argc < 2 scatta alla prima esecuzione, ed ecco perché vale la pena scriverlo.

Errori comuni

  • Leggere argv[1] senza controllare argc. Comportamento indefinito quando non sono stati passati argomenti, e il crash più comune in quest'area.
  • Far partire il ciclo da i = 0. Così elabori il nome del programma come se fosse un dato.
  • Trattare argv[1] come un numero. È testo finché non lo converti.
  • Usare atoi sull'input dell'utente. Non c'è modo di distinguere un argomento errato da uno zero.
  • Confrontare stringhe con ==. Usa strcmp.
  • Scrivere nelle stringhe di argv. Modificarle è consentito dallo standard, ma lo spazio è limitato e dipende dalla piattaforma; copiale in un tuo buffer se devi cambiare qualcosa.

Domande frequenti

Cosa sono argc e argv in C?

Sono i due parametri di int main(int argc, char *argv[]). argc è il numero di argomenti da riga di comando, compreso il nome del programma; argv è un array di quegli argomenti sotto forma di stringhe. argv[0] è il nome del programma, argv[1] è il primo argomento vero e argv[argc] è sempre NULL.

Come si passano argomenti a un programma C?

Scrivili dopo il nome dell'eseguibile nel terminale: ./program hello 42. Così ottieni argc == 3, con argv[1] la stringa "hello" e argv[2] la stringa "42". Nell'editor di questa pagina gli argomenti si inseriscono nel pannello Args invece che in un terminale.

Come si converte un argomento da riga di comando in int in C?

Usa strtol: char *end; long n = strtol(argv[1], &end, 10); poi verifica che end non punti ancora all'inizio (niente è stato letto) e che *end sia '\0' (nessun carattere in più alla fine). atoi è più breve ma restituisce 0 sia per "0" sia per "banana", quindi non può segnalare un errore.

Cos'è argv[0] in C?

Il nome con cui il programma è stato invocato, di solito ./program o il percorso completo. È utile nei messaggi di utilizzo (fprintf(stderr, "usage: %s FILE\n", argv[0])) perché il messaggio corrisponda a come l'utente l'ha davvero chiamato. In casi insoliti può essere una stringa vuota, quindi non dare per scontato che ci sia.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA