Macros with a Variable Number of Arguments in C

With this lesson we conclude the treatment of macros in C language by addressing two new functionalities that the C99 standard has made available.

In particular, starting from C99 it is possible both to omit the arguments of parametric macros and to define macros with a variable number of arguments.

Null Arguments for Parametric Macros in C99

The C99 standard of the C language has introduced the possibility of passing null arguments to a parametric macro.

When you want to pass a null argument, however, the number of commas used must be the same.

Let's clarify with an example. Suppose we have defined the following parametric macro:

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

Normally, we can invoke the ADDITION macro in this way:

int sum = ADDITION(3, 4);

In this case, the ADDITION macro will be replaced with (3 + 4).

Starting from C99 we can omit the arguments. For example we could omit the first argument:

int sum = ADDITION(, 4);

In this case, the ADDITION macro will be replaced with ( + 4) which represents a valid expression. Note that, although the first argument is missing, the comma has been maintained.

Obviously, in omitting the arguments, you must always pay attention to the fact that the replaced code is valid. In the previous example, if we had omitted the second argument, the resulting code would have been (3 + ) which is not a valid expression.

Definition

Null Arguments for Parametric Macros in C99

Starting from the C99 standard, in C language it is possible to omit the arguments of macros.

In omitting the arguments, however, it is necessary to maintain the number of commas.

Null Arguments and Macro Operators

When null arguments are used and the latter appear as operands of a macro operator, particular rules apply.

In case the argument is an operand of the string conversion operator, the result will be an empty string.

For example, suppose we have the following macro:

#define PRINT_NAME(name) printf("Name: %s\n", #name)

Normally, we can invoke the PRINT_NAME macro in this way:

PRINT_NAME(Mario);

In this case, the PRINT_NAME macro will be replaced with printf("Name: %s\n", "Mario").

If we omit the argument, the result will be an empty string:

PRINT_NAME();

In this case, the PRINT_NAME macro will be replaced with printf("Name: %s\n", "").

Therefore, when we omit an argument, for example x, the string conversion operator applied to it, #x, will return an empty string: "".

Definition

Null Arguments and String Conversion Operator

When an argument of a macro is omitted and this is an operand of the string conversion operator, the result will be an empty string.

In particular, if x is the omitted argument, the operator #x will return "".

Similarly, also for the concatenation operator, in case of null arguments, particular rules apply.

Suppose we have the following macro:

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

Normally, we can invoke the CONCATENATE macro in this way:

float CONCATENATE(x, y, z);

The result will be float xyz;. If we omit one of the three arguments, the preprocessor, internally, will use a placeholder argument. This placeholder, when concatenated with another argument, will return the argument itself. So, if we try to omit the first argument:

float CONCATENATE(, y, z);

In this case, the CONCATENATE macro will be replaced with float yz;.

Similarly, if we try to omit the other two arguments:

float CONCATENATE(x,,);

In this case, the CONCATENATE macro will be replaced with float x;.

We can also omit all the arguments even if, in this case, the code would not be valid. In fact, the result of CONCATENATE(,,) would be float ;.

Definition

Null Arguments and Concatenation Operator

When an argument of a macro is omitted and this is an operand of the concatenation operator, the preprocessor uses a placeholder argument.

This placeholder argument, concatenated with another argument, will return the argument itself.

If x is the omitted argument while y is a valid argument, the operation x##y will return y.

Macros with a Variable Number of Arguments in C99

In the C89 standard, a macro had to have a fixed number of arguments. However, starting from the C99 standard, it is possible to define macros with a variable number of arguments. In particular a macro can have an unlimited number of arguments.

This functionality was introduced in conjunction with the possibility of defining functions with a variable number of arguments.

The typical example of a function with a variable number of arguments is the printf function:

/* printf used with one argument only */
printf("Hello World!");

/* printf used with two arguments */
printf("The number is: %d\n", 3);

/* printf used with three arguments */
printf("The number %d is equal to %f\n", 3, 3.14);

Macros with a variable number of arguments were introduced precisely to be able to pass a variable number of arguments to a function.

To clarify the concept, let's consider a macro that verifies a condition and, if positive, prints a message with the printf function:

#define CONDITION(condition, ...) \
        ((condition) ? \
            printf("Condition verified: %s\n" #condition) : \
            printf(__VA_ARGS__))

Let's observe the definition in detail. First of all, after the first parameter condition we have inserted ...; this identifier takes the name of ellipsis and indicates that, after the first parameter, the macro can accept a variable number of arguments.

In the body of the macro we have, then, used the special identifier __VA_ARGS__. This identifier represents all the arguments passed to the macro, obviously excluding the first condition.

In this way, the CONDITION macro accepts at least two parameters. The first is the condition to test, all the others are arguments that will be passed to the printf function.

Now let's see an example of using the CONDITION macro:

CONDITION(temperature <= maximum_temperature,
           "Warning: the temperature is too high! %d °C\n", temperature);

In this case, the CONDITION macro will be replaced with:

((temperature <= maximum_temperature) ? 
    printf("Condition verified: %s\n" "temperature <= maximum_temperature") : 
    printf("Warning: the temperature is too high! %d °C\n", temperature));

Therefore, if the variable temperature is less than or equal to the variable maximum_temperature, the message will be printed:

Condition verified: temperature <= maximum_temperature

Otherwise, the message will be printed:

Warning: the temperature is too high! 50 °C
Definition

Macros with a Variable Number of Arguments in C99

Starting from the C99 standard, it is possible to define macros with a variable number of arguments.

To define a macro with a variable number of arguments, the identifier ... is used at the point where you want to accept a variable number of arguments.

To access the arguments passed to the macro, the special identifier __VA_ARGS__ is used.

The syntax is as follows:

#define MACRO_NAME(...) \
        /* body of the macro */

In Summary

In this lesson we have seen two important additional functionalities introduced by the C99 standard for macros.

  • We have seen how it is possible to omit the arguments of parametric macros. In this case, it is necessary to maintain the number of commas.
  • We have seen how it is possible to define macros with a variable number of arguments. In this case, the identifier ... is used to accept a variable number of arguments and __VA_ARGS__ to access the arguments passed to the macro.

With this lesson the treatment of macros in C language concludes. In the next lesson, we will address another important use of the C preprocessor: conditional compilation.