One difference between Pascal and the C language as commonly implemented in the days before standardization is that every user-defined Pascal function will always be passed with a fixed set of arguments, while C functions might sometimes be passed more and sometimes fewer, and there was no need for a function to indicate, even retrospectively, how many arguments it had actually been passed. One could thus do something like:
void test(mode, x)
int mode,x;
{
if (mode==0)
printf("Hello there!\n");
else
printf("X was %d\n", x);
}
and safely invoke the function using either test(0)
or test(1, 42)
. Or, if one liked, test(0, 57)
though the value 57 would be ignored.
Because Pascal functions always knew how what arguments would be passed, a called function could clean up the stack for the benefit of the caller. Because there are usually more function calls than functions that are called, having the called function clean up the stack would generally save code. When using common semantics, however, a C compiler couldn't generate a function epilogue that would clean up arguments passed by the caller because it wouldn't know how many bytes to pop. While it would have been possible to pass such information as an extra argument, that would have been more expensive than simply leaving cleanup to the calling function. *The 8086 includes a special form of the "RET" instruction, RET n
, to facilitate this by popping n
bytes of arguments off the stack.
Note that in the days before standardization, there were some C implementations that used the Pascal-style calling convention. On such implementations, a call to "printf" that didn't include enough formatting specifiers to consume all the arguments would jump the rails. The C Standard opted to require that compilers handle calls to functions that are declared as accepting variable argument lists in a way that will ignore extra arguments, perhaps by processing a function like:
void variadicTest(int foo, ...);
as though it were:
void variadicTest(int foo, char *__arguments);
and then having a call like:
variadicTest(1234, 1.2f, 23, "Hello");
get processed as:
struct { double p1, int p2, char *p3} __temp24601 =
{1.2f, 23, "Hello"};
variadicTest(123, (char*)&__temp24601;
Thus, a conforming C implementation for the 8086 could use the Pascal calling convention and take advantage of the RET n
instruction. Functions generated by such an implementation, however, would fail if called from code that expects them to use the C calling convention. The Standard requires that a call to a non-variadic function for which a prototype is visible be treated the same as one for which a prototype is not visible, thus making it impossible for compilers to use the principle "use Pascal convention if a prototype or new-style definition is visible, or the C convention otherwise". Some compilers did support a "pascal" keyword for that purpose, and use of it could improve performance, but it would make code non-portable.