Discussion:
why the different results?
(too old to reply)
lallous
2010-02-24 12:25:00 UTC
Permalink
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
Igor Tandetnik
2010-02-24 12:46:55 UTC
Permalink
Post by lallous
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
lallous
2010-02-24 13:43:23 UTC
Permalink
Thanks Igor for the clarification.

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

--
Elias
Post by Igor Tandetnik
Post by lallous
bool abc1(
unsigned long &v1,
unsigned long &v2,
unsigned long &v3,
...)
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
Igor Tandetnik
2010-02-24 13:54:23 UTC
Permalink
Post by lallous
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
Loading...