Funzioni con un Numero Variabile di Argomenti in Linguaggio C
Numero di Argomenti Variabile
Arrivati fino a questo punto, abbiamo visto come dichiarare e definire funzioni che accettano (o meno) un certo numero di parametri di ingresso.
Tuttavia, al lettore più attento non sarà sfuggito che esistono delle funzioni, come la funzione di libreria printf
, che può essere invocata con un numero differente di argomenti a seconda dei casi.
Consideriamo il seguente stralcio di codice:
// Invochiamo la funzione printf con un solo argomento
printf("Ciao\n");
// Invochiamo la funzione printf con due argomenti
printf("Il risultato è %d\n", 42);
// Invochiamo la funzione printf con tre argomenti
printf("Il risultato è %d e il resto è %d\n", 42, 1);
Nell'esempio di sopra abbiamo invocato la funzione printf
prima con un solo argomento, poi con due e infine con tre argomenti! Il che farebbe pensare che la funzione printf
sia quasi un'eccezione alla regola.
In realtà, non è così. Infatti, in C è possibile definire funzioni che accettano un numero variabile di argomenti attraverso le funzionalità messe a disposizione dalla libreria standard. Vediamo come fare.
Funzione che accetta un numero variabile di argomenti
Per prima cosa, per poter definire una funzione che accetta un numero variabile di argomenti, dobbiamo adoperare un operatore speciale, ...
, chiamato ellissi.
Questo operatore deve essere posizionato alla fine della lista dei parametri formali della funzione. Ad esempio, la seguente dichiarazione di funzione è corretta:
void funzione(int a, int b, ...);
In questo caso, la funzione funzione
accetta due parametri di tipo int
e un numero variabile di argomenti di qualsiasi tipo.
Tuttavia, non è possibile definire una funzione che accetta un numero variabile di argomenti senza specificare almeno un parametro formale. Infatti, la seguente dichiarazione di funzione non è corretta:
// Errore:
// almeno un parametro formale deve essere specificato
void funzione(...);
Inoltre le ellissi non possono essere utilizzate in altri punti della lista dei parametri formali. Ad esempio, le seguenti dichiarazioni di funzione non sono corrette:
// Errore:
// L'ellissi non può essere posta all'inizio della lista dei parametri formali
void funzione(..., int a);
// Errore:
// L'ellissi non può essere posta in mezzo alla lista dei parametri formali
void funzione(int a, ..., int b);
Operatore Ellissi
L'operatore ellissi ...
è un operatore speciale in C che consente di definire funzioni con un numero variabile di argomenti. Deve essere posizionato alla fine della lista dei parametri formali e deve essere preceduto da almeno un parametro formale. Non può essere utilizzato in altri punti della lista dei parametri formali.
La sintassi corretta per definire una funzione con un numero variabile di argomenti è la seguente:
tipo_ritorno funzione(tipo1 a, tipo2 b, ...);
Una volta che abbiamo definito una funzione con un numero variabile di argomenti, possiamo invocarla normalmente facendo attenzione però a rispettare alcune condizioni:
- Gli argomenti corrispondenti ai parametri formali espliciti devono essere forniti in ordine e con il tipo corretto.
- Gli argomenti corrispondenti agli argomenti variabili possono essere forniti in qualsiasi ordine e con qualsiasi tipo, ma devono essere forniti in numero sufficiente affinché la funzione possa funzionare correttamente.
Ad esempio, supponiamo di voler dichiarare una funzione somma
che effettui la somma di un certo numero di interi. La dichiarazione della funzione potrebbe essere la seguente:
int somma(int n, ...);
In questo caso, n
rappresenta il numero di argomenti che vogliamo sommare. La funzione somma
accetterà quindi un numero variabile di argomenti di tipo int
.
A questo punto, possiamo invocare la funzione somma
in questo modo:
int risultato = somma(3, 1, 2, 3);
Con il primo argomento indichiamo il numero di argomenti che vogliamo sommare, 3
, e con i successivi tre argomenti indichiamo i numeri da sommare, 1
, 2
e 3
.
Adesso che abbiamo visto come dichiarare e invocare una funzione con un numero variabile di argomenti, vediamo come possiamo implementarla.
Libreria stdarg.h
Per implementare una funzione con un numero variabile di argomenti, dobbiamo utilizzare alcune macro (vedremo le macro in seguito) definite nel file di intestazione stdarg.h
. Questo file di intestazione fa parte della libreria standard del linguaggio C e fornisce le funzionalità necessarie per gestire gli argomenti variabili.
Le principali macro definite in stdarg.h
sono:
va_start
: inizializza un oggetto di tipova_list
per accedere agli argomenti variabili.va_arg
: recupera il valore del prossimo argomento variabile di un tipo specificato.va_end
: termina l'accesso agli argomenti variabili.
Vediamo come adoperare queste macro per implementare la funzione somma
che abbiamo dichiarato in precedenza.
#include <stdio.h>
#include <stdarg.h>
int somma(int n, ...) {
int risultato = 0;
// Inizializza un oggetto di tipo va_list
va_list args;
va_start(args, n);
// Somma gli argomenti variabili
for (int i = 0; i < n; i++) {
risultato += va_arg(args, int);
}
// Termina l'accesso agli argomenti variabili
va_end(args);
return risultato;
}
int main() {
int risultato = somma(3, 1, 2, 3);
printf("La somma è: %d\n", risultato);
return 0;
}
Come si può vedere dall'esempio, l'implementazione della funzione somma
consiste in vari passaggi:
- Dichiariamo una variabile di tipo
va_list
chiamataargs
per accedere agli argomenti variabili. Il tipova_list
è un cosiddetto tipo opaco, il che significa che non importa sapere esattamente come è fatto, ma solo come usarlo. Infatti, tutte le implementazioni del linguaggio C possono implementareva_list
in modo differente. - Inizializziamo
args
con la macrova_start
, passando come secondo argomento il numero di argomenti formali che precede l'ellissi. In questo caso, il numero di argomenti formali èn
. Adesso risulta chiaro perché non possiamo definire una funzione con un numero variabile di argomenti senza specificare almeno un parametro formale: non sapremmo da dove cominciare a contare gli argomenti variabili! - Recuperiamo il valore del prossimo argomento variabile con la macro
va_arg
, specificando il tipo dell'argomento che vogliamo recuperare. In questo caso, vogliamo sommare solo argomenti di tipoint
, quindi passiamoint
come secondo argomento ava_arg
. - Infine, chiudiamo l'accesso agli argomenti variabili con la macro
va_end
.
Esistono altre macro definite in stdarg.h
, ma quelle che abbiamo visto sono le più comuni e quelle che ci servono per implementare una funzione con un numero variabile di argomenti.
Ricapitolando:
Macro per Gestire Argomenti Variabili
Le macro principali definite in stdarg.h
per gestire gli argomenti variabili sono:
-
va_start
: inizializza un oggetto di tipova_list
per accedere agli argomenti variabili:va_list args; va_start(args, numero_argomenti);
-
va_arg
: recupera il valore del prossimo argomento variabile di un tipo specificato:tipo argomento = va_arg(args, tipo);
-
va_end
: termina l'accesso agli argomenti variabili:va_end(args);
In Sintesi
In questa lezione abbiamo introdotto un meccanismo molto potente fornito dal linguaggio C per definire funzioni con un numero variabile di argomenti.
In particolare abbiamo visto che:
- Per definire una funzione con un numero variabile di argomenti, dobbiamo utilizzare l'operatore ellissi
...
alla fine della lista dei parametri formali. - Oltre all'operatore ellissi, dobbiamo dichiarare almeno un parametro formale.
- Per implementare una funzione con un numero variabile di argomenti, dobbiamo utilizzare le macro definite nel file di intestazione
stdarg.h
, in particolareva_start
,va_arg
eva_end
. - La macro
va_start
inizializza un oggetto di tipova_list
per accedere agli argomenti variabili. - La macro
va_arg
recupera il valore del prossimo argomento variabile di un tipo specificato. - La macro
va_end
termina l'accesso agli argomenti variabili. - Le macro
va_start
,va_arg
eva_end
devono essere utilizzate in questo ordine per garantire un corretto accesso agli argomenti variabili.