Old style C function declaration

匿名 (未验证) 提交于 2019-12-03 01:27:01

问题:

Here's a simple function delcared and defined using old style syntax:

#include  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?

回答1:

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.



回答2:

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.



回答3:

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:

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.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!