C Programming

va_arg in C (variadic arguments)

The C libraries provide a wide range of functions to cover the needs of a programmer and also provide the ability to create our own personal functions to meet the specific needs of each case.

Among the function types provided by this language are the “variadic” functions. These function types have the flexibility to contain a dynamic or variable number of input arguments.

In this Linux Hint article, the va_arg macro, which is a basic component of this function type and is used to retrieve the data from the input arguments, is explained in detail.

We will see a detailed explanation of its operation and syntax. Then, we will put what we have learned into practice in a practical example where we will create a variadic function step by step with code fragments and images that show how the va_arg macro works in the C language.

va_arg macro syntax

type va_arg ( va_list ap, type )

Definition of a Variadic Function

Before we go into more detail about the macro va_arg, let us take a quick look at what a variadic function is.

These functions do not have a fixed number of input arguments but the number of these arguments is adapted to what the programmer sends with each call.

An example of this is the widely used variadic function printf(), whose input arguments can be just a string, a string and a variable, or a pointer, or several of them.

Next, we will see how to define a variadic function:

type function ( type variable, ...);

As we see in the definition, when creating a function of this type, we must specify in its declaration at least one declared input argument and its type, followed by a comma-separated ellipsis representing the variable or unknown arguments.

The variables and macros that use the variadic functions, such as va_arg, are defined in the “stdarg.h” header. So, to use them, we need to include them in our “.c” code or its header as follows:

#include <stdarg.h>

Next, let us look in detail at what the macros that make up the variadic function are all about.

Input Arguments and Macros of a Variadic Function

In variadic functions, a number of macros and variable types are used to process the input arguments that the programmer sends with each call. These macros and their use within the function are shown below.

va_list ap

The ap object is of type va_list and stores information about the input arguments. Then, it points to the current position in the retrieval order of the list input data.

Once declared, the va_list object must be initialized with the va_start macro.

void va_start ( va_list ap, last );

The macro va_start is called first when a variadic function is called. It initializes the object ap which points to the first unknown argument in the list.

type va_arg ( va_list ap, type );

This macro returns the next input argument pointed to by ap from the argument list. The returned data type is specified in type.

As soon as va_arg retrieves the data, ap increments its value by the reference to the next input argument.

This macro does not return a default value indicating that the list of input arguments has reached its end. So, the programmer must make sure that a safe method is generated that indicates whether there are still arguments in the list that can be extracted or not.

A safe method consists of including, in each call to the variadic function, a constant and unique value that can be interpreted in the body of the function as an indicator of “no more parameters left” in the last input parameter.

void va_end ( va_list ap );

Once all arguments have been retrieved, each cycle of va_start must be terminated with va_end before the variadic function returns. Otherwise, there is information on the stack with the data of the current call, which could lead to errors in the next call of the function

We have already seen each of the macros that are part of the argument retrieval in a variadic function. Now, let us see an example of how using the va_arg macro to retrieve data from the input arguments is implemented in this type of function.

How to Create a Variadic Function Step by Step and Retrieve its Input Arguments with the Macro va_arg() in the C Language

In this example we explain step by step how to create a variadic function and retrieve its input arguments – using the macro va_arg.

In the first step, we create the variadic function, which we will call get_arguments().

Both the output and the declared input argument arg_1 will be of type double. The statement will look like this:

double get_arguments ( double arg_1, ... );

After declaring the function with its output and input types, we continue with the development of the function body.

In the next step, we will create an array of 10 elements of type double with the name get_arg. In this array, we will store the data of the input argument, which we will retrieve with the macro va_arg.

We will also create the variable “a”, which is of type int and will serve as an identifier for the elements of the get_arg array.

double get_arg [10];

int a = 1;

In the next step, we create an object of type va_list, which we will call “ap”.

This object is initialized with the macro va_start and passes as first argument, the name of the previously created object ‘ap’; and as second argument the name of the last known input variable, in this case “arg_1”.

va_list ap;

va_start (ap, arg_1);

It is important to note that the first argument, and in this case the only one known by the function, is not included in the “ap” list, so its recovery is done in the same way as for a non-variadic function.

In this case we store it in element number 1 of the get_arg array.

get_arg [a] = R1;

Next, create a while loop to retrieve the input arguments with the macro va_arg.

In this loop, repeat this until the macro va_arg gets the value -1 or “e”, which will be the indicator for the “last argument”.

In each cycle of the loop, the message “Argument retrieved:” is printed by the printf() function, followed by the value of the retrieved data.

Then, the identifier “a” is incremented by 1 and the macro va_arg is called, which retrieves the next input argument and stores it in the array element get_arg referenced by “a”.

while( get_arg [ a ] !=  e)
      {
    printf ("Recoveredargument %d", a);
    printf (": %f\n", get_arg [a]);
       a++;
    get_arg [ a ] = va_arg (ap, double);
        }

When all the data has been retrieved and the program has exited the loop, we must exit the list object “ap” that we created at the beginning of the function with the macro va_end and pass the name of this object as the input argument.

va_end ( ap);

Next, we will see the full code for the variadic function we just created, and the main in which to call the function and declare the variables of type double that we will send as input arguments.

#include <stdio.h>

#include <stdarg.h>

voidget_arguments( double R1,  ...);

double e = -1;

voidmain () {

double arg_1 = 10;
doublearg_2 = 4700;
double arg_3 = 2200;
double arg_4 = 5800;
double arg_5 = 3300;

get_arguments( arg_1, arg_2, arg_3, arg_4,arg_5,  e);
   }  

voidget_arguments( double R1, ...) {

int a = 1;
doubleget_arg [10];
va_listap;
va_start (ap, R1);
get_arg [a] = R1;
while( get_arg [ a ] !=  e){

    printf ("Recoveredargument %d", a);
    printf (": %f\n", get_arg [a]);
          a++;
    get_arg [ a ] = va_arg (ap, double);
          }
va_end (ap);

}

The image below shows the command console with the input arguments retrieved. In this case, the function was called with two input arguments.

The data retrieved for a call with five input arguments.

Problems and Solutions in Retrieving Input Data with the va_arg Macro

The main problem we will encounter in developing a variadic function is that the macro va_arg has no method to inform the programmer of the end of the list of input arguments. So once all the data sent in the call has been retrieved, this macro will return erroneous results every time it is called indefinitely

This means that not only will you get incorrect results, but in cases where the data retrieval is looped, there will be an overflow. Therefore, the programmer must come up with a method to detect the end of the arguments in the list. One of the methods may be to use a constant as the last argument that indicates the end of the list.

Another method is to specify as the first argument the number of parameters to be sent each time the variadic function is called.

Conclusion

In this Linux Hint article, we have given you a detailed and complete explanation of how variadic functions work and how to use the va_arg macro in the C language.

We also explained in detail the use of the other macros that are part of the data recovery in this type of function and showed you step by step how to declare and develop one of them which are a very important resource in this and other programming languages. You can find more articles like this in the Linux Hint search engine.

About the author

Julio Cesar

Julio Cesar is a 42 years old programmer with 8 years of experience in embedded systems development, 6 years developing firmware for user interfaces in C and C++. Additionally he has 2 years of experience developing scripts for network devices and 3 years as developer of high frequency PCB (Printed Circuit Board).