Microsoft KB Archive/119394

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

Article ID: 119394

Article Last Modified on 7/5/2005

-

APPLIES TO

 The C Run-Time (CRT), when used with:  Microsoft Visual C++ 1.0 Professional Edition

 Microsoft Visual C++ 1.5 Professional Edition

 Microsoft Visual C++ 1.0 Professional Edition

 Microsoft Visual C++ 2.0 Professional Edition</li></ul>

 Microsoft Visual C++ 2.1</li></ul>

 Microsoft Visual C++ 4.0 Standard Edition</li></ul>

 Microsoft Visual C++ 5.0 Standard Edition</li></ul>

 Microsoft Visual C++ 6.0 Service Pack 5</li></ul> </li></ul>

-

<div class="notice_section">

This article was previously published under Q119394

<div class="symptoms_section">

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.

<div class="cause_section">

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.

<div class="resolution_section">

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.

<div class="moreinformation_section">

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


 * 1) include <stdio.h>
 * 2) 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: kbcrt kbprb KB119394

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.