K&R-Style Function Declarations: Good or Bad?

D

Dan Pop

In said:
Some compilers at least, in K&R mode, complain about anything other
than int f();

He wasn't talking about K&R C programs, but about standard C programs
using K&R function definitions. It is perfectly legal to have a
K&R function definition and a prototype declaration for it in a standard
C program.

Dan
 
C

Chris Torek

But that would be misleading, given that the type of c is still char:

[long program, reformatted a bit for vertical compression]
f.c:
#include <stdio.h>
int f(c) char c; { return printf("%d\n", (int)c); }
main.c:
int f(int);
int main() {
f(1000);
return 0;
}

I'm not sure about the behaviour of this code in K&R C, which talks about
float parameters getting widened to double, but no mention about char and
short parameters getting promoted to int.

Narrow parameters behave as if converted twice, once with "the
default argument promotions", and then back to the narrow type in
the K&R function definition. Thus, this is the same as an ANSI C
program that reads, for f.c:

#include <stdio.h>
int f(int c0) { char c = c0; return printf("%d\n", (int)c); }

The same trick applies to short and float, of course.

There was at least one implementation of K&R C on a big-endian
machine that failed to "convert back" correctly, so that in any
actual function written as:

f(c) char c; { ... }

&c was the wrong one of the four bytes (it was an 8-bit-char,
32-bit-int compiler). I am not sure if somehow the compiler managed
to use all four bytes when producing c's value in most contexts,
but I do remember that, while some code broke in interesting ways,
a surprising amount still worked anyway. (But everyone who used
that compiler agreed that it was broken, and that &c should have
been the address of the low byte of the widened stack copy of the
value.)
 
A

Alan Balmer

He wasn't talking about K&R C programs, but about standard C programs
using K&R function definitions. It is perfectly legal to have a
K&R function definition and a prototype declaration for it in a standard
C program.

Dan

Missed that. Dunno why we're talking about it anyway ;-) I wouldn't
bother inventing prototypes for K&R function definitions without
changing the definitions as well.
 
J

J. J. Farrell

Eric Sosman said:
J. J. Farrell said:
Michael B. said:
I tend to use rather descriptive names for parameters, so the old style of
declaration appeals to me, as I can keep a declaration within 80 chars:

void * newKlElem (frame_size,num_blocks,num_frames,frame_locator)
size_t frame_size;
unsigned short num_blocks;
unsigned short num_frames;
Kl_frame_locator *locator;
{

[...]

The example you give is also invalid. Unless you have a prototype
in scope at the time of the call, it is not possible for your
function to receive parameters of type <unsigned short>. They will
have been promoted at the time of the call to be either <int> or
<unsigned int> depending on the type sizes in the implementation -
so to make your version portable, you need some ugly compile-time
duplication of the parameters in the function definition, or you
need to explicitly cast the parameters to an appropriate type in
each call.

This is the second time this piece of misinformation has
cropped up in this thread. I can only conclude that people
have become so accustomed to prototyped functions (that's
understandable; they *are* better) that they've completely
forgotten how old-style functions worked.

Apologies for the misinformation. The curious thing is that
I remember what I said from way back then. Whether it was a
peculiarity of a certain compiler, or a failure in the teaching
or learning process, is something now lost in the mists of time.
...
- Pre-Standard functions and calls work in Standard C
just as they do/did in pre-Standard C.

Ahhh - but since they were pre-standard, we can't be sure they
all behaved the same way! I notice that Chris has mentioned a
compiler where my comment was effectively true. Perhaps I (or
whomever I picked this up from) had had dealings with a similar
compiler. Or, more likely, my brain has atrophied.
... Use prototypes.

Invariably. Except, perhaps ... are there any circumstances under
which people think it's appropriate to not use prototypes? The
only one that comes to mind is a generic function pointer.
 
L

lawrence.jones

Dan Pop said:
I'm not sure about the behaviour of this code in K&R C, which talks about
float parameters getting widened to double, but no mention about char and
short parameters getting promoted to int.

As far as I know, all pre-ANSI compilers widened char and short
arguments to int (even if char was unsigned -- the first hint of
value-preserving conversion rules!). Behavior varied for unsigned char
and unsigned short: some compilers didn't support them at all, most
compilers widened them to unsigned int, a few (or maybe just one) used
ANSI-like value-preserving rules and widened them to either int or
unsigned int depending. Behavior also varied for the parameter
declarations -- some compilers converted the widened types back to the
declared types, other compilers quietly rewrote the parameter
declarations using the widened types (much as array declarations are
quietly rewritten as pointer declarations).

-Larry Jones

That's one of the remarkable things about life. It's never so
bad that it can't get worse. -- Calvin
 
D

Dan Pop

In said:
But that would be misleading, given that the type of c is still char:

[long program, reformatted a bit for vertical compression]
f.c:
#include <stdio.h>
int f(c) char c; { return printf("%d\n", (int)c); }
main.c:
int f(int);
int main() {
f(1000);
return 0;
}

I'm not sure about the behaviour of this code in K&R C, which talks about
float parameters getting widened to double, but no mention about char and
short parameters getting promoted to int.

Narrow parameters behave as if converted twice, once with "the
default argument promotions", and then back to the narrow type in
the K&R function definition. Thus, this is the same as an ANSI C
program that reads, for f.c:

#include <stdio.h>
int f(int c0) { char c = c0; return printf("%d\n", (int)c); }

The same trick applies to short and float, of course.

Not to float! K&R1, page 205:

C converts all float actual parameters to double, so formal parameters
declared float have their declaration adjusted to read double.

It is precisely this text that prompted by doubts about the handling of
char and short parameters in K&R C, because their declarations could be
adjusted to int for precisely the same rationale as floats being
adjusted to doubles, but I couldn't find any mention about this hapenning.

Dan
 
D

Dan Pop

In said:
Invariably. Except, perhaps ... are there any circumstances under
which people think it's appropriate to not use prototypes? The
only one that comes to mind is a generic function pointer.

This is also the only counterexample I am aware of. But it doesn't
affect function *definitions*.

I'm also too lazy to type the void in the main function definition, when
I don't need its parameters, which turns it into a K&R function
definition. But that's OK, since I never call main() in my programs and
its actual caller couldn't care less about how I define it.

Dan
 
D

Dan Pop

In said:
As far as I know, all pre-ANSI compilers widened char and short
arguments to int (even if char was unsigned -- the first hint of
value-preserving conversion rules!).

This part is clear, the argument conversions are well documented by K&R1.
Behavior varied for unsigned char
and unsigned short: some compilers didn't support them at all, most
compilers widened them to unsigned int, a few (or maybe just one) used
ANSI-like value-preserving rules and widened them to either int or
unsigned int depending.

These types were a mess back then. K&R1 doesn't support them so the only
hope to write portable code was to ignore them completely.
Behavior also varied for the parameter
declarations -- some compilers converted the widened types back to the
declared types, other compilers quietly rewrote the parameter
declarations using the widened types (much as array declarations are
quietly rewritten as pointer declarations).

This is the less clear part. K&R1 Appendix A explicitly mentions float
parameter declarations being rewritten as double, but doesn't mention
char and short. OTOH, there is the following text at page 42, right
after the definition of the argument conversions:

This is why we have declared function arguments to be int and double,
even when the function is called with char and float.

Kernighan is obviously talking about function parameters. It's for this
reason that I never declared function parameters as char, short or
float back when I was writing K&R C programs. And now it's too late to
investigate what would have happened if I did ;-(

Dan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,122
Messages
2,570,717
Members
47,283
Latest member
VonnieEwan

Latest Threads

Top