Parameter Types in Old-Style Function Definitions
Kee Hinckley
nazgul at alphalpha.com
Sat Sep 8 15:34:08 AEST 1990
In article <2689 at dataio.Data-IO.COM> bright at Data-IO.COM (Walter Bright) writes:
>In article <10391 at pt.cs.cmu.edu> hjelm at g.gp.cs.cmu.edu (Mark Hjelm) writes:
><What is the compiler allowed/required to do for this:
>< 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 <stdio.h>
#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 at alphalpha.com
nazgul at 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.
More information about the Comp.std.c
mailing list