PRB: Using References with va_* Macros from stdarg.h

Last reviewed: July 31, 1997
Article ID: Q119394
The information in this article applies to:
  • The C Run-time (CRT) included with: - Microsoft C/C++ for MS-DOS, version 7.0 - Microsoft Visual C++ for Windows, versions 1.0, 1.5 - Microsoft Visual C++ 32-bit Edition, versions 1.0, 2.0, 2.1, 4.0, 5.0

SYMPTOMS

In Microsoft C++, if you use functions that accept a variable number of arguments, you may encounter problems when trying to use the va_* family of functions to access the parameters if the second parameter used for the va_start macro is a reference type.

CAUSE

This problem is caused by the way that the va_start macro is defined and the way that the C++ language handles taking the address of a reference. Applying the "address of" operator to a reference type results in a pointer to the object that is being referred to. The va_start macro takes the address of the last named parameter to locate subsequent parameters. When the last named parameter is a reference, this causes problems because the macro is no longer referring to the current call stack but whatever follows the object being referred to, which could be a previous call stack or a global memory object.

RESOLUTION

The workaround is to redefine the va_start macro to use inline assembly to subvert the C++ language.

NOTE: This solution is not portable and will require changing if you intend your source code to be used on non-Intel platforms.

MORE INFORMATION

The va_start macro is used in conjunction with the va_arg macro to "walk" the stack to get the parameters passed to the variable argument list. The va_start macro is defined as follows:

   #define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )

where va_list is defined as a char * on Intel platforms. The macro parameter "ap" is of type va_list. The problem arises from taking the address of the second parameter, "v", if v is a reference type. The net result of this macro being expanded is that ap is supposed to point to the first of the variable parameters. Casting v to a non-reference type intuitively seems like the logical solution, but because the result of a cast is not an l-value, the compiler returns an error message.

NOTE: The only way to get an l-value from a cast is to cast the value to a reference type, which results in the same problem.

Sample Code

The sample code below demonstrates a solution for this problem:

   /* Compile options needed:  none
   */

   #include <stdio.h>
   #include <stdarg.h>

   // Uncomment the following lines to work-around the problem:
   //
   // #ifdef va_start
   // #undef va_start
   //
   // #ifdef _WIN32
   // #define va_start(ap,v) {int var= _INTSIZEOF(v); \
   //                __asm lea eax,v __asm add eax,var __asm mov ap,eax \
   //                }
   // #else
   // #define va_start(ap,v) { int var=_INTSIZEOF(v);\
   //                __asm lea ax,v __asm add ax,var __asm mov ap,ax\
   //                }
   // #endif
   // #endif

   void numprint( int &first ... )
   {
     va_list ap;

     va_start( ap, first );
     printf("%d\n", first );
     int ival = va_arg( ap, int );
     printf("%d\n", ival );
     double dval = va_arg( ap, double );
     printf( "%.2f\n", dval );
     va_end(ap);
   }

   void main()
   {
     int i=100,j=1000;
     float f=999.99;

     numprint( i,j,f );
   }


Additional query words: ellipsis
Keywords : CRTIss
Version : 7.0 1.0 1.5 2.0 2.1 4.0 5.0
Platform : MS-DOS NT WINDOWS
Issue type : kbprb


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.

Last reviewed: July 31, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.