Path: utzoo!utgpu!watserv1!watmath!att!pacbell!pacbell.com!decwrl!wuarchive!cs.utexas.edu!yale!mintaka!spdcc!tauxersvilli!alphalpha!nazgul From: nazgul@alphalpha.com (Kee Hinckley) Newsgroups: comp.std.c Subject: Re: Parameter Types in Old-Style Function Definitions Keywords: parameter, function Message-ID: <1990Sep8.053408.2005@alphalpha.com> Date: 8 Sep 90 05:34:08 GMT References: <10391@pt.cs.cmu.edu> <2689@dataio.Data-IO.COM> Organization: asi Lines: 230 In article <2689@dataio.Data-IO.COM> bright@Data-IO.COM (Walter Bright) writes: >In article <10391@pt.cs.cmu.edu> hjelm@g.gp.cs.cmu.edu (Mark Hjelm) writes: >< f(a, b) >< float a; >< char b; >< { >< } > >It is semantically equivalent to: > f(atmp,btmp) > double atmp; > int btmp; That part I understand. I have a related question though. Consider the following function, defined in K&R: foo(c, i) char c; int i; {} Clearly the function expects to pick two ints up off of the stack. Now consider the same function, declared and used from ANSI C (or C++). extern foo(char c, int i); foo('g', 0xF0F0); As far as I can tell the ANSI spec (but I'm just reading it through the second edition K&R book) doesn't explicitly address the compatibility issue raised here. In other words, in some implementations the compiler may do an explicit promotion of 'char c' to 'int', even though it retains the correct semantics. Whereas other compilers may actually put a single byte on the stack, in which case the call will not work properly. I note that the X Intrinsics seem to recognize this problem and define both "Wide" and "Narrow" prototypes for the functions, where the "Wide" prototypes explicitly redefine all small integral types to "int" and the "Narrow" ones leave them be. The default, for compatibility reasons, is "Wide". So I guess the question is. Does the ANSI spec mandate that the above be compatible, mandate that they aren't, or not say? And, does it make any difference whether the definition of the function (same syntax) is compiled using a K&R or an ANSI compiler? Here is a sample program to test this with, consisting of two files, the first of which must be compiled with a prototyping compiler, the second of which may be compiled with or without one. Bind them together and see what happens. /* * * p1.c * * Here are the combinations: * declared defined * exact prototype exact prototype * exact prototype wide prototype * exact prototype no prototype * wide prototype exact prototype * wide prototype wide prototype * wide prototype no prototype * no prototype exact prototype * no prototype wide prototype * no prototype no prototype * * and, since I here GNU cheats on this and uses ANSI semantics even * if the definition is K&R style * * exact prototype K&R wide * wide prototype K&R wide * no prototype K&R wide */ extern void EE(char c, int i); extern void EW(char c, int i); extern void EN(char c, int i); extern void WE(int c, int i); extern void WW(int c, int i); extern void WN(int c, int i); extern void NE(); extern void NW(); extern void NN(); extern void EG(char c, int i); extern void WG(int c, int i); extern void NG(); void main() { EE('x', 999); EW('x', 999); EN('x', 999); WE('x', 999); WW('x', 999); WN('x', 999); NE('x', 999); NW('x', 999); NN('x', 999); EG('x', 999); WG('x', 999); NG('x', 999); } /* * p2.c * * Here are the combinations: * declared defined * exact prototype exact prototype * exact prototype wide prototype * exact prototype no prototype * wide prototype exact prototype * wide prototype wide prototype * wide prototype no prototype * no prototype exact prototype * no prototype wide prototype * no prototype no prototype * * and, since I here GNU cheats on this and uses ANSI semantics even * if the definition is K&R style * * exact prototype K&R wide * wide prototype K&R wide * no prototype K&R wide */ #include #ifdef __STDC__ void EE(char c, int i) { printf("Exact Exact: '%c', %d\n", c, i); } void EW(int c, int i) { printf("Exact Wide: '%c', %d\n", c, i); } #endif void EN(c, i) char c; int i; { printf("Exact None: '%c', %d\n", c, i); } #ifdef __STDC__ void WE(char c, int i) { printf("Wide Exact: '%c', %d\n", c, i); } void WW(int c, int i) { printf("Wide Wide: '%c', %d\n", c, i); } #endif void WN(c, i) char c; int i; { printf("Wide None: '%c', %d\n", c, i); } #ifdef __STDC__ void NE(char c, int i) { printf("None Exact: '%c', %d\n", c, i); } void NW(int c, int i) { printf("None Wide: '%c', %d\n", c, i); } #endif void NN(c, i) char c; int i; { printf("None None: '%c', %d\n", c, i); } void EG(c, i) int c, i; { printf("Exact Wide-K&R: '%c', %d\n", c, i); } void WG(c, i) int c, i; { printf("Wide Wide-K&R: '%c', %d\n", c, i); } void NG(c, i) int c, i; { printf("None Wide-K&R: '%c', %d\n", c, i); } --- Here is the result of running this on an Apollo. Exact Exact: 'x', 999 Exact Wide: '', 65498744 Exact None: '', 65498744 Wide Exact: '', 7864320 Wide Wide: 'x', 999 Wide None: 'x', 999 None Exact: '', 7864320 None Wide: 'x', 999 None None: 'x', 999 Exact Wide-K&R: '', 65471463 Wide Wide-K&R: 'x', 999 None Wide-K&R: 'x', 999 Basically Exact is Exact and it isn't compatible with anything that wasn't defined without a prototype, Wide and None are identical. This risks compatibility with old code (you have to be careful how you define things and you never want to inconsistantly use prototypes in a single program). On the other hand, it enhances compatibility with other languages. -- Alphalpha Software, Inc. | motif-request@alphalpha.com nazgul@alphalpha.com |----------------------------------- 617/646-7703 (voice/fax) | Proline BBS: 617/641-3722 I'm not sure which upsets me more; that people are so unwilling to accept responsibility for their own actions, or that they are so eager to regulate everyone else's.