Macro con un Numero Variabile di argomenti in linguaggio C

Con questa lezione concludiamo la trattazione delle macro in linguaggio C affrontando due nuove funzionalità che lo standard C99 ha messo a disposizione.

In particolare, a partire dal C99 è possibile sia omettere gli argomenti delle macro parametriche sia definire delle macro con un numero variabile di argomenti.

Argomenti nulli per le macro parametriche in C99

Lo standard C99 del linguaggio C ha introdotto la possibilità di passare degli argomenti nulli ad una macro parametrica.

Quando si vuole passare un argomento nullo, tuttavia, il numero di virgole adoperate deve essere lo stesso.

Chiariamo con un esempio. Supponiamo di aver definito la seguente macro parametrica:

#define ADDIZIONE(a, b) (a + b)

Normalmente, possiamo invocare la macro ADDIZIONE in questo modo:

int somma = ADDIZIONE(3, 4);

In tal caso, la macro ADDIZIONE verrà sostituita con (3 + 4).

A partire dal C99 possiamo omettere gli argomenti. Ad esempio potremmo omettere il primo argomento:

int somma = ADDIZIONE(, 4);

In tal caso, la macro ADDIZIONE verrà sostituita con ( + 4) che rappresenta un'espressione valida. Si noti che, sebbene il primo argomento sia mancante, la virgola è stata mantenuta.

Ovviamente, nell'omettere gli argomenti, bisogna sempre prestare attenzione al fatto che il codice sostituito sia valido. Nell'esempio di prima, se avessimo omesso il secondo argomento, il codice risultante sarebbe stato (3 + ) che non è una espressione valida.

Definizione

Argomenti nulli per le macro parametriche in C99

A partire dallo standard C99, in linguaggio C è possibile omettere gli argomenti delle macro.

Nell'omettere gli argomenti, tuttavia, è necessario mantenere il numero di virgole.

Argomenti nulli e operatori delle macro

Quando si utilizzano gli argomenti nulli e questi ultimi appaiono come operandi di un operatore macro, si applicano delle regole particolari.

Nel caso in cui l'argomento sia un operando dell'operatore di conversione stringa, il risultato sarà una stringa vuota.

Ad esempio, supponiamo di avere la macro che segue:

#define STAMPA_NOME(nome) printf("Nome: %s\n", #nome)

Normalmente, possiamo invocare la macro STAMPA_NOME in questo modo:

STAMPA_NOME(Mario);

In tal caso, la macro STAMPA_NOME verrà sostituita con printf("Nome: %s\n", "Mario").

Se omettiamo l'argomento, il risultato sarà una stringa vuota:

STAMPA_NOME();

In tal caso, la macro STAMPA_NOME verrà sostituita con printf("Nome: %s\n", "").

Quindi, quando omettiamo un argomento, ad esempio x, l'operatore di conversione stringa applicato ad esso, #x, restituirà una stringa vuota: "".

Definizione

Argomenti nulli e operatore di conversione stringa

Quando si omette un argomento di una macro e questo è un operando dell'operatore di conversione stringa, il risultato sarà una stringa vuota.

In particolare, se x è l'argomento omesso, l'operatore #x restituirà "".

Analogamente, anche per l'operatore di concatenazione, in caso di argomenti nulli, si applicano regole particolari.

Supponiamo di avere la macro che segue:

#define CONCATENA(a, b, c) a##b##c

Normalmente, possiamo invocare la macro CONCATENA in questo modo:

float CONCATENA(x, y, z);

Il risultato sarà float xyz;. Se omettiamo uno dei tre argomenti, il preprocessore, internamente, utilizzerà un argomento segnaposto. Questo segnaposto, quando concatenato con un altro argomento, restituirà l'argomento stesso. Per cui, se proviamo ad omettere il primo argomento:

float CONCATENA(, y, z);

In tal caso, la macro CONCATENA verrà sostituita con float yz;.

Analogamente, se proviamo ad omettere gli altri due argomenti:

float CONCATENA(x,,);

In tal caso, la macro CONCATENA verrà sostituita con float x;.

Possiamo anche omettere tutti gli argomenti anche se, in questo caso, il codice non sarebbe valido. Infatti, il risultato di CONCATENA(,,) sarebbe float ;.

Definizione

Argomenti nulli e operatore di concatenazione

Quando si omette un argomento di una macro e questo è un operando dell'operatore di concatenazione, il preprocessore utilizza un argomento segnaposto.

Questo argomento segnaposto, concatenato con un altro argomento, restituirà l'argomento stesso.

Se x è l'argomento omesso mentre y è un argomento valido, l'operazione x##y restituirà y.

Macro con un numero variabile di argomenti in C99

Nello standard C89, una macro doveva avere un numero fisso di argomenti. Tuttavia, a partire dallo standard C99, è possibile definire delle macro con un numero variabile di argomenti. In particolare una macro può avere un numero illimitato di argomenti.

Questa funzionalità è stata introdotta in congiunzione con la possibilità di definire funzioni con un numero variabile di argomenti.

L'esempio tipico di una funzione con un numero variabile di argomenti è la funzione printf:

/* printf usata con un solo argomento */
printf("Ciao Mondo!");

/* printf usata con due argomenti */
printf("Il numero è: %d\n", 3);

/* printf usata con tre argomenti */
printf("Il numero %d è pari a %f\n", 3, 3.14);

Le macro con un numero variabile di argomenti sono state introdotte proprio per poter passare un numero variabile di argomenti ad una funzione.

Per chiarire il concetto, consideriamo una macro che verifica una condizione e, in caso positivo, stampa un messaggio con la funzione printf:

#define CONDIZIONE(condizione, ...) \
        ((condizione) ? \
            printf("Condizione verificata: %s\n" #condizione) : \
            printf(__VA_ARGS__))

Osserviamo la definizione nel dettaglio. Per prima cosa, dopo il primo parametro condizione abbiamo inserito ...; questo identificatore prende il nome di ellipsis e indica che, dopo il primo parametro, la macro può accettare un numero variabile di argomenti.

Nel corpo della macro abbiamo, poi, adoperato l'identificatore speciale __VA_ARGS__. Questo identificatore rappresenta tutti gli argomenti passati alla macro, ovviamente escluso il primo condizione.

In questo modo, la macro CONDIZIONE accetta almeno due parametri. Il primo è la condizione da testare, tutti gli altri sono argomenti che verranno passati alla funzione printf.

Adesso vediamo un esempio di utilizzo della macro CONDIZIONE:

CONDIZIONE(temperatura <= temperatura_massima,
           "Attenzione: la temperatura è troppo alta! %d °C\n", temperatura);

In questo caso, la macro CONDIZIONE verrà sostituita con:

((temperatura <= temperatura_massima) ? 
    printf("Condizione verificata: %s\n" "temperatura <= temperatura_massima") : 
    printf("Attenzione: la temperatura è troppo alta! %d °C\n", temperatura));

Per cui, se la variabile temperatura è minore o uguale alla variabile temperatura_massima, verrà stampato il messaggio:

Condizione verificata: temperatura <= temperatura_massima

Altrimenti, verrà stampato il messaggio:

Attenzione: la temperatura è troppo alta! 50 °C
Definizione

Macro con un numero variabile di argomenti in C99

A partire dallo standard C99, è possibile definire delle macro con un numero variabile di argomenti.

Per definire una macro con un numero variabile di argomenti, si adopera l'identificatore ... nel punto in cui si vuole accettare un numero variabile di argomenti.

Per accedere agli argomenti passati alla macro, si adopera l'identificatore speciale __VA_ARGS__.

La sintassi è la seguente:

#define NOME_MACRO(...) \
        /* corpo della macro */

In Sintesi

In questa lezione abbiamo visto due importanti funzionalità aggiuntive introdotte dallo standard C99 per le macro.

  • Abbiamo visto come sia possibile omettere gli argomenti delle macro parametriche. In tal caso, è necessario mantenere il numero di virgole.
  • Abbiamo visto come sia possibile definire delle macro con un numero variabile di argomenti. In tal caso, si adopera l'identificatore ... per accettare un numero variabile di argomenti e __VA_ARGS__ per accedere agli argomenti passati alla macro.

Con questa lezione si conclude la trattazione delle macro in linguaggio C. Nella prossima lezione, affronteremo un altro importante utilizzo del preprocessore C: la compilazione condizionale.