Dr. Roger Ianjamasimanana

The stdarg.h in C for managing variable arguments

By Dr. Roger Ianjamasimanana

1. What is stdarg.h in C

The stdarg.h header in C provides functionality for managing variable numbers of function arguments (variadic functions). This is useful when the number or types of arguments cannot be determined at compile time or when you need a function to accept a flexible set of parameters.

2. Predefined type

  • va_list – Represents an object of type "variable argument list".

Before you can access any of the variable arguments passed to a function, you declare a variable of type va_list, which internally keeps track of your position among the arguments.

3. Macros in stdarg.h

  1. va_start(va_list param, last_fixed_param)
  2. Initializes the traversal of the variable argument list. The first parameter to va_start is the va_list that you declared. The second parameter (last_fixed_param) is the name of the last fixed (non-variadic) parameter in the function’s parameter list. Important: this macro must never be redefined as a function.

  3. va_arg(va_list param, type)
  4. Expands to an expression that has the specified type and the value of the current argument in the list. After you call va_arg, the va_list is automatically advanced to the next argument. This macro must never be redefined as a function.

  5. va_end(va_list param)
  6. Terminates the use of the variable argument list. You must call va_end for every va_list that was initialized by va_start (or by va_copy in some C standards). This macro must be called before exiting the function that uses the variable argument list.

    4. Examples of using stdarg.h in C

    4.1. A simple variadic function to print integers

    The follwing function demonstrates how to handle a variable number of integer arguments using the stdarg.h library in C. It begins by calling va_start on the va_list variable, then iterates through the supplied arguments, retrieving each integer in turn via va_arg. For each integer, it prints out the argument’s index and value. Finally, it cleans up the list by calling va_end, ensuring correct resource management.

    #include <stdarg.h>
    #include <stdio.h>
    
    void print_integers(int count, ...)
    {
        va_list args;
        va_start(args, count);
    
        for(int i = 0; i < count; i++)
        {
            // Retrieve an int from the list of arguments
            int value = va_arg(args, int);
            printf("Value %d: %d\n", i + 1, value);
        }
    
        va_end(args);
    }
    
    int main(void)
    {
        print_integers(3, 10, 20, 30);
        return 0;
    }

    4.2. Summation function

    #include <stdio.h>
    #include <stdarg.h>
    
    int sum(int num_args, ...);
    
    int main(void)
    {
        printf("Total = %d\n", sum(4, 1, 2, 3, 4));
        printf("Another total = %d\n", sum(3, 27, 34, 55));
        return 0;
    }
    
    int sum(int num_args, ...)
    {
        int total = 0;
        int i;
        va_list arg_pointer;
    
        // Initialize arg_pointer to the start of the variable args
        va_start(arg_pointer, num_args);
    
        // Loop over the number of arguments, retrieving each one
        for (i = 0; i < num_args; i++)
        {
            int value = va_arg(arg_pointer, int);
            total += value;
        }
    
        // Clean up
        va_end(arg_pointer);
    
        return total;
    }

    The num_args parameter indicates how many additional integers to expect. Each va_arg call retrieves an int and increments the internal pointer. Finally, va_end cleans up after the loop.


    4.3. A simplified "print" function

    Here’s how you might write a minimal function that behaves somewhat like printf, handling a format string and multiple arguments:

    #include <stdio.h>
    #include <stdarg.h>
    
    void imprime(const char *format, ...);
    
    int main(void)
    {
        int a = 3;
        int b = 8;
        char message[] = "Hello";
    
        imprime("a=%d b=%d\n", a, b);
        imprime("Message=%s\n", message);
    
        return 0;
    }
    
    void imprime(const char *format, ...)
    {
        va_list arg_pointer;
        const char *p;
        int int_value;
        char *str_value;
    
        va_start(arg_pointer, format);
    
        for (p = format; *p != '\0'; p++)
        {
            if (*p != '%')
            {
                putchar(*p);
                continue;
            }
    
            // If it's a '%', get the next character to determine what to print
            switch (*++p)
            {
                case 'd':
                    int_value = va_arg(arg_pointer, int);
                    printf("%d", int_value);
                    break;
    
                case 's':
                    str_value = va_arg(arg_pointer, char *);
                    printf("%s", str_value);
                    break;
    
                default:
                    // If it's not recognized, just print the character
                    putchar(*p);
                    break;
            }
        }
    
        va_end(arg_pointer);
    }

    In this example, imprime iterates over each character of format. When a '%' is found, it checks the next character ('d' for integers or 's' for strings) and retrieves the corresponding argument with va_arg.


feature-top
Readers’ comment
feature-top
Log in to add a comment
🔐 Access