Newsgroups: comp.lang.c Path: utzoo!utgpu!jarvis.csri.toronto.edu!dgp.toronto.edu!flaps From: flaps@dgp.toronto.edu (Alan J Rosenthal) Subject: Re: cdecl keyword Message-ID: <8804152130.AA04256@caboto.dgp.toronto.edu> Organization: University of Toronto Date: Fri, 15 Apr 88 15:30:33 EDT Recently, I (flaps@dgp.toronto.edu (Alan J Rosenthal)) wrote that before ANSI C it was generally accepted that variadic functions were allowed in C so long as you didn't use any parameters not actually passed. For instance, I claimed that this was valid: main() { f(1); ... } int f(a,b) int a,b; { if(a == 1) return(5); ... } Doug Gwyn, Karl Heuer, and Henry Spencer all took objection to this. They said that dmr's recent posting said that in K&R C variadic functions are forbidden (an aside from the point of that item in that posting, which was that this is an inconsistency in K&R C, since printf exists). In particular, Henry said that I was confusing what you can get away with in certain common implementations with what is guaranteed by C. I disagree. (I would like to point out that I have done this in non-unix C implementations as well. I no longer do, as putting a dummy extra argument is clearer and has no disadvantages.) I am interested in hearing about implementations (pre-ansi only, please!) in which the above code segment causes problems. I would be surprised if there is none, but I would like to point out that for most rules in C there exists AN implementation that violates it. I would also like to draw your attention to the first sentence in the fourth paragraph in section 4.3 on page 71 of K&R, which states that "It is generally safe to deal with a variable number of arguments if the called function doesn't use an argument which was not actually supplied, and if the types are consistent." Note that the main objection to the portability of variadic functions is that "there is no portable way for the called function to determine how many arguments were actually passed to it" (previous paragraph), not that the arguments may be pushed in reverse order a la Pascal. Also, just to throw some more fuel into the fire, I would like to point out that the idea of rules which defined C and guaranteed portable behaviour was not present in K&R in the sense in which this idea is conceived today. For example, on page 163 we find the function: error(s1, s2) /* print error message and die */ char *s1, *s2; { printf(s1, s2); printf("\n"); exit(1); } Looking above it to see how it is called, we see the line "#define NULL 0" and a second argument of "NULL", _not_ "(char *)NULL". This can be explained by assuming that sizeof(char *) == sizeof(int) and that, even if (int)0 is an invalid char *, it will never be examined by printf due to the format string so it's ok. It can also be explained by assuming that even if sizeof(char *) != sizeof(int) things will work out ok due to my above assumption, plus a little extra assumption that grabbing garbage off the stack and passing it around is fine so long as you don't ever look at it. (Admittedly this last justification suggests calling it without a second argument altogether, unless you're worried about stack underflow.) ajr -- extern volatile const noalias wellbehaved int f();