KnowledgeBase Archive

An Archive of Early Microsoft KnowledgeBase Articles

View on GitHub

Q58490: Variable Arguments of Type CHAR Incorrectly Retrieved

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.

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Copyright Microsoft Corporation 1986-2002.