Old style C function declaration

2019-01-13 16:44发布

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?

4条回答
等我变得足够好
2楼-- · 2019-01-13 17:08

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.

查看更多
▲ chillily
3楼-- · 2019-01-13 17:09

Actually, the compiler effectively does have a prototype in scope if it encounters the definition of the function error() before encountering its use (this is why old C programmers often order function definitions in files according to their order of use). Consequently, the 110 can get converted to (char*)110 (not that it would matter on a machine where sizeof(int) == sizeof(char*)). It would be interesting to see what would happen on a machine where sizeof(int) != sizeof(char*).

查看更多
在下西门庆
4楼-- · 2019-01-13 17:10

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.

查看更多
贼婆χ
5楼-- · 2019-01-13 17:18

Actually there is a conversion of 110 to int, this conversion is done by fprintf, when fprint reads "%d", it tries to convert the corresponding parameter to int. Another point is that the function expect pointers, i.e, memory address, and pointers are integers. If a string had been passed instead of 110, there would still be a number printed, the address of the string at run-time.

查看更多
登录 后发表回答