7/31/08

Variable Arguments in C-C++ with STDARG Library

Variable Arguments in C/C++ with STDARG Library

Introduction

The ability to pass a varying number of arguments to a subroutine is a very powerful feature of a programming language. Very few languages support this ability. C and C++ do provide this capability, and they give the programmer a nice level of freedom in how functions can be designed using this capability.

In C, two of the most commonly used functions utilize this variable argument capability. The two functions are the standard input/output routines, printf() and scanf(). Below are several examples of using these functions, each example demonstrating that a different number of arguments are passed:

printf ("This is a test"); printf ("The result is: %d\n",num); printf ("%d %10.2f %c %-04d %s",num1,f1,chr,num2,str);  scanf ("%f",&num); scanf ("%d/%d/%d",&month,&day,&year); 

Other common languages include the ability for system functions to pass a varying number of arguments. For example, Pascal's read(), write(), readln() and writeln() procedures. These are the only procedures in Pascal that have this ability, however -- user-defined procedures or functions cannot.

The stdarg library in C/C++ gives all programmers the ability to pass a varying number of arguments. The process to do this requires the use of a small number of predefined functions and macros that are provided in the stdarg library, as well as following a few simple coding rules.

The balance of this document will describe the various features in the stdarg library, the coding rules one must follow, as well as showing a variety of examples of functions passing a variable number of arguments.

Code Requirements

In the design of your variable argument functions, you must be able to determine exactly how many arguments are being passed for each invocation of the function. The famous stdio functions printf() and scanf() use a very sophisticated method of determining the number of arguments that relys heavily on the user's proper use of the functions.

Both printf() and scanf() count the number of percent-signs (%) found inside the formatting string, which is always the first argument to the function. The functions are assuming the number of arguments that follow this formatting string exactly matches the number of percent-signs found in the string.

The functions also use the accompanying code letter (d for integers, f for floats and doubles, c for characters, etc), to indicate what type of data-value the associated argument will be.

It's reasonable to note at this point that this is where many C programs go bad because the programmer inadvertently lies to the printf() or scanf() function and either doesn't indicate the proper number of percent-signs, or improperly designates the correct code letter. If either the indication of the number of arguments or the code type is wrong, this can cause major problems in the handling of the arguments and usually results in a segmentation fault.

Most variable-argument functions don't have to be quite as sneeky as printf() and scanf() are. There are therefore two common techniques that can be used to indicate exactly how many arguments are passed to a function:

  • Use an argument to indicate the number of arguments that will follow. This is usually simply an integer that exactly equals the number of values that appear in the following arguments. This technique is best used when only a small number of arguments is planned to help eliminate the possibility of the programmer miscounting them.

  • Use a special sentinel argument that indicates the end of the argument list. This is usually a value that normally does not occur in the sequence of argument values. For a series of numeric arguments, for example, the sentinel might be the value 0, or maybe -1. For a series of string arguments, the sentinel might be a NULL or an empty string ("").

Use of the stdarg Library

To use a function with a variable number of arguments, you must first of all include the library that will help your deal with the processing of the varying arguments, stdarg.h. You must therefore include this library in your program with a #include statement, along with your library references, like the following:

#include

To declare a function that will use a variable number of arguments, you must make sure you have at least one defined argument in the argument list; that is, a specific argument with a name and type associated with it. You can have as many real arguments as you would like; you just have to have at least one.

Following the last defined argument, you should place the C-symbol that indicates your use of a variable number of arguments, the ellipsis, or ... (three periods in a row). An example would look like this:

type functionName (defined_args, ...)

You control the access of the variable arguments with the following definitions/functions/macros provided in stdarg. All of these references must only appear inside the body of a function that will have a variable number of arguments (as denoted by the ellipsis):

  • va_list varname;
    This is the name of a structure that will maintain the information about the variable argument list, and it is therefore used in each of the following functions/macros. NOTE that the va stands for variable arguments. And example might be:

    va_list args;

  • va_start (varname, last_defined_arg)
    This function initializes the processing of the variable argument list. The first parameter corresponds to the variable name you used when you defined the va_list structure. The second parameter corresponds to the variable name of the last defined argument in the argument list in the function's definition.

    This function must be called before you try to reference any of the variable arguments. If it is called more than one time inside a function, the processing of the list of variable arguments will start over again at the first variable argument. An example would be:

    void foo (int num, ...)
    {
    va_list args;

    va_start (args, num);

  • va_arg (varname, typename)
    This function is the one you use to access the value of each variable argument. The first parameter corresponds to the variable name you used when you defined the va_list structure. The second parameter corresponds to the type of the value that is passed.

    This is why you have not only have to be able to predeterine the number of arguments that will be passed, but also the type of each individual argument.

    The va_arg() function will return to you to the corresponding value of the next variable argument in turn. This simply means that the first time va_arg() is called, it will return the value of the first variable argument passed in this invocation of the function, the second time it is called it will return the value of the second variable argument passed, and so forth. Here are some examples:

    int num1 = va_arg(args,int);
    float num2 = va_arg(args,float);

  • va_end (varname)
    This function you call at the very end of your function, right before you return. It's job is to place the system stack in order, and prepare to return to the calling routine. It only has to be called once and it doesn't have to be called each time you'd like to reuse the argument list. Here is an example:

    va_end (args);

You simply combine this declaration and three function/macro calls to properly process the variable argument list. The following sections show several example programs that utilize variable parameter lists using the various methods previously described.

No comments: