Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Old style C function declaration

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?

like image 358
Oxdeadbeef Avatar asked Jan 03 '11 01:01

Oxdeadbeef


People also ask

How do you declare function in C?

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.

Does C require function declaration?

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.

What are the 3 common parts of declaring functions?

The function body has three parts: an optional declarative part, an executable part, and an optional exception-handling part.

Which is the format of function declaration?

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.


2 Answers

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,

  1. 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.

  2. 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.

like image 82
Charlie Martin Avatar answered Nov 02 '22 19:11

Charlie Martin


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.

like image 27
Matthew Flaschen Avatar answered Nov 02 '22 20:11

Matthew Flaschen