From: lallous on
Hello

I have this code:

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

bool abc1(
unsigned long &v1,
unsigned long &v2,
unsigned long &v3,
...)
{
va_list va;
va_start(va, v3);
printf("<abc1> ");
for (int i=0;i<5;i++)
{
printf("%02x ", va_arg(va, int) & 0xff);
}
printf("</abc1>\n");
va_end(va);
return true;
}

bool abc2(
unsigned long v1,
unsigned long v2,
unsigned long v3,
...)
{
va_list va;
va_start(va, v3);
printf("<abc2> ");
for (int i=0;i<5;i++)
{
printf("%02x ", va_arg(va, int) & 0xff);
}
printf("</abc2>\n");
va_end(va);
return true;
}

int main()
{
unsigned long r1=0, r2=0, r3=0;

abc1(r1, r2, r3,
0x54,0x01,0x00,0x00,0x00,0x58,0x05,0x00,0x00,0x00,0x28,0x02,0x00,0x00,0x00,0x16,0xff,
0xff,0xff,0xff,0xff);
abc2(r1, r2, r3,
0x54,0x01,0x00,0x00,0x00,0x58,0x05,0x00,0x00,0x00,0x28,0x02,0x00,0x00,0x00,0x16,0xff,
0xff,0xff,0xff,0xff);

return 0;
}

The only difference between abc1() and abc2() is that the first three
arguments are passed by value or by reference. Now the output is
different, when compiled:

C:\Temp>cl32 abc.cpp & abc
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01
for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.

abc.cpp
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.

/out:abc.exe
abc.obj
<abc1> 00 00 88 94 01 </abc1>
<abc2> 54 01 00 00 00 </abc2>

Please advise.

--
Elias

From: Igor Tandetnik on
lallous wrote:
> bool abc1(
> unsigned long &v1,
> unsigned long &v2,
> unsigned long &v3,
> ...)

Your code exhibits undefined behavior:

18.7p3 ... The parameter parmN is the identifier of the rightmost parameter in the variable parameter list of the function definition (the one just before the ...). If the parameter parmN is declared with a function, array, or reference type, or with a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.

As a practical matter, va_start(va, v3) takes an address of v3 and expects it to point into the stack where abc1's parameter list is. But when you take an address of a reference, you get the address of the referred-to value, not the address of the reference itself. So, instead of starting where v3 is on the stack and walking through the parameter list, abc1 is starting from where r3 is on the stack of main() and walking though whatever data just happens to sit above it on the stack.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
From: lallous on
Thanks Igor for the clarification.

It seems g++ (GCC) 3.4.4 has no problem with that.

--
Elias

"Igor Tandetnik" <itandetnik(a)mvps.org> wrote in message
news:#DQG19UtKHA.732(a)TK2MSFTNGP06.phx.gbl...
> lallous wrote:
>> bool abc1(
>> unsigned long &v1,
>> unsigned long &v2,
>> unsigned long &v3,
>> ...)
>
> Your code exhibits undefined behavior:
>
> 18.7p3 ... The parameter parmN is the identifier of the rightmost
> parameter in the variable parameter list of the function definition (the
> one just before the ...). If the parameter parmN is declared with a
> function, array, or reference type, or with a type that is not compatible
> with the type that results when passing an argument for which there is no
> parameter, the behavior is undefined.
>
> As a practical matter, va_start(va, v3) takes an address of v3 and expects
> it to point into the stack where abc1's parameter list is. But when you
> take an address of a reference, you get the address of the referred-to
> value, not the address of the reference itself. So, instead of starting
> where v3 is on the stack and walking through the parameter list, abc1 is
> starting from where r3 is on the stack of main() and walking though
> whatever data just happens to sit above it on the stack.
> --
> With best wishes,
> Igor Tandetnik
>
> With sufficient thrust, pigs fly just fine. However, this is not
> necessarily a good idea. It is hard to be sure where they are going to
> land, and it could be dangerous sitting under them as they fly
> overhead. -- RFC 1925

From: Igor Tandetnik on
lallous wrote:
> It seems g++ (GCC) 3.4.4 has no problem with that.

"The program appears to work" is included in the range of undefined behavior.

It appears GCC provides certain (non-standard) compiler intrinsics to help programs determine where named arguments end and variadic arguments begin:

http://www.delorie.com/gnu/docs/gcc/gccint_135.html

GCC's implementation of va_start uses these and ignores its second parameter.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925