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