Article: Q58490
Product(s): See article
Version(s): 5.10 6.00 6.00a | 5.10 6.00 6.00a
Operating System(s): MS-DOS | OS/2
Keyword(s): ENDUSER | s_quickc buglist5.10 buglist6.00 buglist6.00a | mspl13_c
Last Modified: 19-JAN-1991
If a procedure with variable parameters uses either va_start or va_arg
on a variable of type CHAR, all subsequent parameters retrieved with
va_arg will return an incorrect value.
Below there are two separate discussions, one for the workaround, and
one for a description of the problem.
Discussion of the Workaround
----------------------------
The following program illustrates the problem:
#include <stdio.h>
#include <stdarg.h>
#define VA_START_CHAR(ap,v) (ap) = ((va_list) (&v) + sizeof (int))
void main (void) ;
void foo (int, ...) ;
void goo (char, ...) ;
void main (void)
{
int x = 3 ;
char j = 'f' ;
foo (5, j, x) ;
goo (j, 5, x) ;
}
void foo (int s, ...)
{
char c ;
int i ;
va_list arg_marker ;
va_start (arg_marker, s) ;
c = va_arg (arg_marker, char) ;
i = va_arg (arg_marker, int) ;
printf ("FOO (%d, %c, %d)\n", s, c, i) ;
va_start (arg_marker, s) ;
c = (char) va_arg (arg_marker, int) ;
i = va_arg (arg_marker, int) ;
printf ("FOO (%d, %c, %d)\n\n", s, c, i) ;
}
void goo (char s, ...)
{
int i, i2 ;
va_list arg_marker ;
va_start (arg_marker, s) ;
i = va_arg (arg_marker, int) ;
i2 = va_arg (arg_marker, int) ;
printf ("GOO (%c, %d, %d)\n", s, i, i2) ;
VA_START_CHAR (arg_marker, s) ;
i = va_arg (arg_marker, int) ;
i2 = va_arg (arg_marker, int) ;
printf ("GOO (%c, %d, %d)\n", s, i, i2) ;
}
The output of the program is as follows:
FOO (5, f, 768)
FOO (5, f, 3)
GOO (f, 1280, 768)
GOO (f, 5, 3)
The program shows both the typical way to retrieve variable arguments,
and a way around the problem with a variable of type CHAR.
If a function has a type CHAR as being one of the variable arguments
and attempts to retrieve it with a call to va_arg (arg_marker, char),
the correct value will be returned. Unfortunately, the arg_marker is
incremented to an incorrect value. The next time that a variable is
referenced off of arg_marker, va_arg will return an incorrect value.
An easy way around this is to have va_arg return an INT, then just
typecast the return value of va_arg to a CHAR.
The function foo demonstrates both ways of retrieving arguments and,
as you can see from the output, using the INT and then typecasting
that value to a CHAR does work.
A second problem with variable arguments and variables of type CHAR is
when the first argument (the one actually defined) is of type CHAR. A
call to va_start is supposed to set arg_marker to the next parameter.
Again, arg_marker is set to an incorrect value. Any subsequent calls
to va_arg will return an incorrect value.
One way around this is to redefine va_start to the following macro:
#define VA_START_CHAR(ap,v) (ap) = ((va_list) (&v) + sizeof (int))
This works in the special case of a CHAR as being the value that you
are attempting to set the frame reference (arg_maker in the program
and ap in the macro) relative to. It also works if the type sent is an
INT.
Discussion of the Problem (Description of the Problem)
------------------------------------------------------
All CHAR values are pushed onto the stack in two bytes, rather than
one. In STDARG.H, both va_start and va_arg use a sizeof operation to
decide how much to move the frame reference. Because sizeof returns a
1 (one) for a type CHAR and the value actually takes two bytes on the
stack, va_start initializes the frame reference incorrectly, and
va_arg increments the frame reference incorrectly.
When we force va_start to use a type INT, it moves the frame reference
to the correct position, and when we use an INT in va_arg, it forces
the frame reference to be incremented by the correct amount.