[comp.sys.ibm.pc] MS-C 5.0 var_args bug?

james@bigtex.uucp (James Van Artsdalen) (06/11/88)

I am attempting to use the ANSI var_args support in MS-C 5.0, and have
encountered what appears to be a bug in the stdarg.h header library.  Has
anyone else run across this?

The problem is that Microsoft used sizeof() to determine the distance
between objects on the stack, even though the stack size of objects is often
not the same as it would be elsewhere.  In particular, sizeof(char) is 1
(by definition), but while char occupies one byte in .data or .bss space,
it occupies two bytes on the stack.  Hence, if the fixed parameter in your
function is a char, or any but the last variable argument is char, the
Microsoft macros fail.

This illustrates both problems.

f(fixed)
char fixed;
{
	va_list ap;
	char c;

	va_start(ap, fixed);
	c = va_arg(ap, char);
}

Using the standard definitions below,

#define va_start(ap,v) ap = (va_list)&v + sizeof(v)
#define va_arg(ap,t) ((t*)(ap += sizeof(t)))[-1]

the &v will generate the address of fixed, and sizeof(v) will add one,
leaving ap set one byte too low: it points to the top half of the word that
"fixed" is in, not to the first parameter.  Similarly, in the va_arg()
reference, ap is incremented by sizeof(t), or one, leaving ap pointing
one byte too low again (ap should be left pointing at the next argument, if
there is one).

Presumably the problem occurs with structs too, but since I don't believe
in passing structs as parameters, testing is left as an exercise to the
reader.  :-)

My proposed solution is below.  If any sizeof() is odd, it is incremented.
I could have said ((sizeof(v) + 1) & ~1, but the code below is a little
clearer to me at a glance.  This looks bad, but it generates nearly optimal
code, and works, which is what counts.

#define va_start(ap,v) ap = ((va_list)&v) + (sizeof(v) + (sizeof(v) & 1))
#define va_arg(ap,t) ( *(t*)( (ap += (sizeof(t) + (sizeof(t) & 1))) - \
                                     (sizeof(t) + (sizeof(t) & 1))) )
-- 
James R. Van Artsdalen   ...!ut-sally!utastro!bigtex!james   "Live Free or Die"
Home: 512-346-2444 Work: 328-0282; 110 Wild Basin Rd. Ste #230, Austin TX 78746

aronoff@garfield (Avram Aronoff) (06/13/88)

In article <2664@bigtex.uucp> james@bigtex.uucp (James Van Artsdalen) writes:
>I am attempting to use the ANSI var_args support in MS-C 5.0...
>
>The problem is that Microsoft used sizeof()...
>...In particular, sizeof(char) is 1
>...but...
>it occupies two bytes on the stack.  Hence, if the fixed parameter in your
>function is a char, or any but the last variable argument is char, the
>Microsoft macros fail.
>
>/* ... */ char c; /* ... */ c = va_arg(ap, char); /* ... */

The problem lies not in the Microsoft macros, but in your code (WS :-)
Since the char parameter is part of a va_list, there is no prototype that says
it's being passed as a char. Therefore, it is widened to int by the standard
conversions before it is passed as a parameter.

In general, chars, shorts, and floats can not be used in va_arg. Instead, use
int or double, and if necessary, truncate by cast or assignment.

As far as structures, this would be a problem only if sizeof(struct a) was
different from its size on the stack. Hmmm...
(Mad dash to try struct { char a; } and struct { char a[3]; } :-)

							Hymie