PRB: VARARG.C Example Does Not Work with /DUNIX and /AL or /AC

ID Number: Q73688

6.00 6.00a 6.00ax | 6.00 6.00a

MS-DOS | OS/2

Summary:

SYMPTOMS

The program example VARARG.C included in the online Help for

Microsoft C versions 6.0, 6.0a, and 6.0ax and QuickC versions 2.5

and 2.51 does not work correctly in the large and compact memory

models with the symbol "UNIX" defined. This problem results from

the fact that the larger pointer size in these memory models is not

accounted for in the program.

CAUSE

The sample program VARARG.C does not work correctly because it

fails to account for the type conversion of the first argument. In

compact or large memory model, the first int passed to the function

is converted to a 4-byte value (char * is 4 bytes in compact and

large model). Because the called function only retrieves 2-byte

ints off the stack, the second call to va_args() will retrieve an

invalid value and the average will be incorrect.

RESOLUTION

The first parameter in a UNIX style variable argument list is

declared as "char *". Because of this, the first argument will

always be coerced to a character pointer. In cases where the

argument is a string or a variable the same size as a character

pointer, this is not a problem. However, in cases where the first

argument is not the same size, you must somehow account for this.

The following are two methods for working around this situation:

1. Add a dummy argument at the beginning of the argument list that

will take the place of the char * (for example, NULL). This will

always work.

-or-

2. When calling va_arg() the first time from the function, use

char* as the size and cast it to the appropriate type. This will

only work if the variable required is of size smaller than

char*.

More Information:

In the sample code below, the UNIX compatible portions of VARARG.C

have been modified to work correctly. In each call to "average," a

NULL is passed as the first parameter. The function then retrieves the

address of a char * into the dummy variable called pointer. The

remainder of the arguments are the retrieved until the value -1 is

reached, at which time the (correct) average is calculated.

Sample Code

-----------

/* VARARG.C illustrates passing a variable number of arguments using

* the following macros:

* va_start va_arg va_end

*

* and the UNIX types:

* va_alist va_dcl

*/

#include <stdio.h>

#include <varargs.h>

int average( va_list );

void main()

{

/* Call with 3 integers (-1 is used as terminator). */

printf( "Average is: %d\n", average( NULL, 2, 3, 4, -1 ) );

/* Call with 4 integers. */

printf( "Average is: %d\n", average( NULL, 5, 7, 9, 11, -1 ) );

/* Call with just -1 terminator. */

printf( "Average is: %d\n", average( NULL, -1 ) );

}

int average( va_alist )

va_dcl

{

char *pointer;

int i, count, sum;

va_list marker;

va_start( marker ); /* Initialize variable arguments */

pointer = va_arg(marker, char *);

for( sum = count = 0; (i = va_arg( marker, int)) != -1; count++ )

sum += i;

va_end( marker ); /* Reset variable arguments */

return( sum ? (sum / count) : 0 );

}

Additional reference words: 2.50 6.00 6.00a 6.00ax