Path: utzoo!mnetor!uunet!lll-winken!lll-tis!ames!oliveb!sun!gorodish!guy From: guy@gorodish.Sun.COM (Guy Harris) Newsgroups: comp.lang.c Subject: Re: use of varargs....portable??? Message-ID: <48966@sun.uucp> Date: 9 Apr 88 22:19:11 GMT References: <206@vedge.UUCP> Sender: news@sun.uucp Lines: 159 In article <206@vedge.UUCP>, lai@vedge.UUCP (David Lai) writes: > Perhaps this has been harped on before, but I seem to have discovered > some interesting differences between 2 C compilers (sun3 and sun4) that > make varargs not so portable. If you know of any 'portable' varargs > implementations, please let me know. How about Sun-3 and VAX? The following program: #include struct goober1 { char c; }; struct goober2 { int i; int j; int k; }; main() { struct goober1 goober1_1, goober1_2; struct goober2 goober2_1, goober2_2; goober1_1.c = 11; goober2_1.i = 1; goober2_1.j = 2; goober2_1.k = 3; goober1_2.c = 22; goober2_2.i = 100; goober2_2.j = 200; goober2_2.k = 300; varargs_func(2, goober1_1, 17, goober2_1, 34); varargs_func(3, goober1_1, 17, goober2_1, 34, goober1_2, 51); varargs_func(4, goober1_1, 17, goober2_1, 34, goober1_2, 51, goober2_2, 68); } int varargs_func(va_alist) va_dcl { va_list ap; register int i; register int count; struct goober1 one; struct goober2 two; register int num; va_start(ap); count = va_arg(ap, int); printf("count = %d\n", count); for (i = 0; i < count; i++) { if (!(i&01)) { one = va_arg(ap, struct goober1); printf("goober1: c = %d\n", one.c); } else { two = va_arg(ap, struct goober2); printf("goober2: i = %d, j = %d, k = %d\n", two.i, two.j, two.k); } num = va_arg(ap, int); printf("num = %d\n", num); } } gives different answers on a VAX running 4.3BSD and a Sun-3 running SunOS 4.0 - *neither* of which is correct! (A VAX running System V will probably give the same wrong answer as a VAX running 4.3BSD; a VAX running VMS may very well give a different answer.) You see, on the VAX, "sizeof (struct goober1)" is 1, while on the Sun-3 (and, I suspect, on most other 68K boxes), "sizeof (struct goober1)" is 2. It appears, however, that *both* compilers align arguments pushed onto the stack on 4-byte boundaries, so that the value of "ap" should be bumped by 4 when fetching a "struct goober1", not by 1 (as is done on the VAX) or 2 (as is done on the Sun-3 and probably on other 68Ks). Another way of putting this is "passing structures to varargs functions doesn't work, in general". Sad, but true. It may be possible to fix some or all of these problems in current C; the 68K/VAX problem can be fixed by having the "va_arg" macro round the size of the object up to a multiple of 4 bytes before adding it to "ap". The problem of machines that don't pass structures simply by putting them in-line after the other arguments (what do other machines that pass arguments in registers, such as the MIPS or Pyramid, do here?) may require ANSI C to solve it; if you declare a function as being a function taking a variable number of arguments, e.g. #include ... extern int varargs_func(int count, ...); ... int varargs_func(int count, ...) { va_list ap; register int i; struct goober1 one; struct goober2 two; register int num; va_start(ap, count); ... } the compiler would know that a different calling sequence should be used and just dump all the arguments past the last "fixed" argument into memory in the form the implementation expects. > The sun3 compiler: ... > structures are passed on the stack subject to: > 1) If the size of the structure is less than 4 > then it will be padded on lower addresses > and passed as size 4, sizeof in the function > will not report the padding > 2) If the structure size is 4 or more, it will > be passed unpadded. Note that, as mentioned above, "sizeof" does report whatever padding is made to satisfy the compiler's general structure alignment rules, so on the Sun-3 structures are padded to a 2-byte boundary and that padding *is* reported by "sizeof". (Also note that "sizeof" should report the *same* size inside and outside the function.) > The sun4 compiler: > appear in a linear block of memory> If you're not passing any floating-point parameters, the first 6 parameters are passed in %o0-%o5; all subsequent parameters are passed on the stack. The SunOS 4.0 and Sys4-3.2L includes #if defined(sparc) # define va_alist __builtin_va_alist #endif and the appearance of "__builtin_va_alist" in the argument list tells the compiler to generate code to dump arguments onto the stack in the proper place for "va_arg" to pick them up. > structures are first copied into some temporary memory, then > passed as pointers. Within the function sizeof shows the > actual structure size, not the pointer size. Which is as it should be; "sizeof (struct foo)" should always give the size of such a structure.