Here's a simple function delcared and defined using old style syntax:
#include <stdio.h>
void
error(message,a1,a2,a3,a4,a5,a6,a7)
char *message;
char *a1,*a2,*a3,*a4,*a5,*a6,*a7;
{
fprintf(stderr,message,a1,a2,a3,a4,a5,a6,a7);
}
int main ()
{
error("[ERROR %d]: %s.\n",110,"Connection timed out");
return 0;
}
It can be compiled and runs correctly to print:
[ERROR 110]: Connection timed out.
I read that this style doesn't have associated prototype, but how can it convert int to char * automatically at runtime and even the provided arguments are fewer than it's declared?
Function Declarations The actual body of the function can be defined separately. int max(int, int); Function declaration is required when you define a function in one source file and you call that function in another file. In such case, you should declare the function at the top of the file calling the function.
Actually, it is not required that a function be declared before use in C. If it encounters an attempt to call a function, the compiler will assume a variable argument list and that the function returns int.
The function body has three parts: an optional declarative part, an executable part, and an optional exception-handling part.
For example, if the my_function() function, discussed in the previous section, requires two integer parameters, the declaration could be expressed as follows: return_type my_function(int x, y); where int x, y indicates that the function requires two parameters, both of which are integers.
Basically, it works because it's too dumb to know better. Old fashioned K&R C basically doesn't check anything. You get away with this because,
it happens that sizeof(int) == sizeof(char *)
on the particular architecture and compiler combination you're using. It's not really converting anything, it just figures 32 bits is 32 bits.
When you put all those arguments on the stack, it just pushed them in. When printf uses them, it just uses the ones if needs and leaves the rest alone; they then disappear when the call returns, and no one's the wiser. However, should you ever happen to try printing seven values where you only passed six arguments, it'll blow up at run time, sometime in creative and unexpected ways.
Passing too few arguments, or the wrong type (you've done both), causes undefined behavior. This is exactly why you should never use old style syntax in new code. If you used new syntax, you would get a "free" prototype from the function definition. In other words:
void
error(char * message,
char * a1, char * a2, char * a3, char * a4, char * a5, char * a6, char * a7)
{
}
is also a prototype.
Using old syntax, you have to provide your own, which you haven't. That means the compiler can't check the calls.
In practice (on your machine), error
is reading the int
from the stack into a char *
. Then, it passes the char *
to fprintf
. But a %d
specifier is used, so fprintf
pops it as an int
. This is more undefined behavior. But it happens to work on your machine; char *
and int
are likely the same size.
error
also reads 5 garbage char *
values off the stack. It then passes these to fprintf
, which it ignores because there are only two conversion specifiers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With