giovedì 7 novembre 2013

Funzioni variadiche, le funzioni con un numero di parametri indefinito

In questo articolo verrà descritte alcune macro presenti nello standard ANSI del linguaggio C/C++, ovvero le macro presenti su <stdarg.h>
Sono delle macro per la gestione delle funzioni variadiche, ovvero delle funzioni con parametri di cui non se ne conosce il numero. La loro utilità la possiamo ritrovare nella funzione standard printf(), la quale fra i parametri accetta una stringa e un numero indefinito di variabili. Il prototipo di questa funzione è:

int printf ( const char * format, ... );


che accetta una stringa e un numero imprecisato di variabili, indicato con "...". Tutti sappiamo il suo funzionamento: essa cerca di prelevare dallo stack tante variabili quante il numero di "%" seguito da una lettera indicante il formato. Questo può dare adito a problemi, infatti se la stringa non è stata formattata correttamente (numero di "%[]" maggiore delle variabili passate effettivamente), la funzione tenta di prelevare dallo stack altre variabili, portando a risultati inattesi e/o indesiderati. 

In realtà una funzione variadica deve sapere quante variabili deve prelevare dallo stack, quindi deve conoscere il numero di parametri, anche se non è esplicitato dal prototipo della funzione. Quindi una funzione variadica deve accettare almeno un parametro, che sia una stringa o altro. 

Per esempio, una funzione variadica potrebbe essere questa:


int sommatoria(int n,...);


dove n è il numero di variabili che dovranno essere passati. La sua implementazione potrebbe essere:


int sommatoria(int n,...){
   int somma = 0;
   int contatore;
   va_list lista;
   va_start(lista,n);
   for (contatore=0;contatore<n;contatore++)
   somma += va_arg(lista,int);
   va_end(lista);
   return somma;
}

e questo un suo esempio di utilizzo:


int somma = sommatoria(4,51,546,51,56);

Commentiamo il codice sopra. va_start(), va_arg() e va_end() non sono delle funzioni, ma delle macro, e quello che fanno non è che una mera sostituzione prima che intervenga il compilatore. va_list invece è una lista di tipi specificati in va_arg(), va_start inizializza questa lista prendendo i dati dallo stack per n volte, mentre va_arg() viene chiamato n volte e ogni volta restituisce una variabile dalla lista, secondo il tipo specificato. va_end() invece provvede a eliminare eventuali bugs e far ritornare il giusto valore alla funzione. 
E' un meccanismo geniale, potente e delicato allo stesso tempo, basta un utilizzo improprio per arrivare a risultati non attesi.

Nessun commento:

Posta un commento