A long, long time ago, back in the K&R, pre-ANSI days, functions looked quite different than they do today.
add_numbers(x, y)
{
return x + y;
}
int ansi_add_numbers(int x, int y); // modern, ANSI C
When you call a function like add_numbers
, there is an important difference in the calling conventions: all types are "promoted" when the function is called. So if you do this:
// no prototype for add_numbers
short x = 3;
short y = 5;
short z = add_numbers(x, y);
What happens is x
is promoted to int
, y
is promoted to int
, and the return type is assumed to be int
by default. Likewise, if you pass a float
it is promoted to double. These rules ensured that prototypes weren't necessary, as long as you got the right return type, and as long as you passed the right number and type of arguments.
Note that the syntax for prototypes is different:
// K&R style function
// number of parameters is UNKNOWN, but fixed
// return type is known (int is default)
add_numbers();
// ANSI style function
// number of parameters is known, types are fixed
// return type is known
int ansi_add_numbers(int x, int y);
A common practice back in the old days was to avoid header files for the most part, and just stick the prototypes directly in your code:
void *malloc();
char *buf = malloc(1024);
if (!buf) abort();
Header files are accepted as a necessary evil in C these days, but just as modern C derivatives (Java, C#, etc.) have gotten rid of header files, old-timers didn't really like using header files either.
Type safety
From what I understand about the old old days of pre-C, there wasn't always much of a static typing system. Everything was an int
, including pointers. In this old language, the only point of function prototypes would be to catch arity errors.
So if we hypothesize that functions were added to the language first, and then a static type system was added later, this theory explains why prototypes are optional. This theory also explains why arrays decay to pointers when used as function arguments -- since in this proto-C, arrays were nothing more than pointers which get automatically initialized to point to some space on the stack. For example, something like the following may have been possible:
function()
{
auto x[7];
x += 1;
}
Citations
On typelessness:
Both languages [B and BCPL] are typeless, or rather have a single data type, the 'word,' or 'cell,' a fixed-length bit pattern.
On the equivalence of integers and pointers:
Thus, if p
is a cell containing the index of (or address of, or pointer to) another cell, *p
refers to the contents of the pointed-to cell, either as a value in an expression or as the target of an assignment.
Evidence for the theory that prototypes were omitted due to size constraints:
During development, he continually struggled against memory limitations: each language addition inflated the compiler so it could barely fit, but each rewrite taking advantage of the feature reduced its size.
int
is a powerfully universal type. Being able to omit it lets you write lots of quick-and-dirty code very succinctly -- "brevity is the soul of wit", as William Shatner said. It's only recently that space (both disk and screen) has become some abundant and programs so large that clean, readable style is preferable to brevity. – Weatherwornauto
here is only to get the compiler to understand thatbar
is supposed to be a variable. I could have usedstatic
as well. – Rimester