“Puts()”函数如何在没有参数的情况下工作?

时间:2010-08-10 13:16:12

标签: c

我在网站上看到了这段代码。

main(i)
{
  gets(&i);
  puts();
}

此代码编译并运行正常!

它从用户那里得到一个字符串作为输入并打印出来!!!!

但是,我的问题是,如何?

(注意puts()函数不包含任何参数!)

5 个答案:

答案 0 :(得分:5)

旧版本的C具有变量和函数的隐式类型,并且此代码使用了该函数和其他一些东西。实际上返回值也非常宽松。

main(i) // i is implicitly an integer (the default type for old C), and normally named argc 
// int main(int i) or void main(int i)
{ // The stack (which lives in high memory but grows downward) has any arguments and
  // probably the environmental variables and maybe even other (possibly blank/filler)
  // stuff on it in addition to the return address for whatever called main and possibly
  // the argument i, but at this point that could either be on the stack just under the
  // return address or in a register, depending on the ABI (application binary interface)


// extern int gets(int) or extern void gets(int)
// and sizeof(int) is probably sizeof(char *)
 gets(&i); // By taking the address of i even if it wasn't on the stack it will be pushed to
           // it so that it will have an address (some processors have addressable registers
           // but they are rarely used by C for many reasons that I won't go into).


           // The address of i is either also pushed onto the stack or put into a register
           // that the ABI says should be used for the first argument of a function, and
           // and then a call is made to gets (push next address to stack; jump to gets)

           // The function gets does what it does, but according to the ABI there are
           // some registers that it can do whatever it wants to and some that it must
           // make sure are the same as they were before it was called and possibly one
           // or more where it is supposed to store a return value.
           // If the address of i was passed to it on the stack then it probably would be
           // restricted from changing that, but if it was passed in a register it may
           // have just been luckily left unchanged.
           // Another possiblity is that since gets returns the string address it was
           // passed is that it returns that in the same location as the first argument
           // to functions is passed.  

 puts();   // Since, like gets, puts takes one pointer argument it will be passed this
           // this argument in the same way as gets was passed it's argument.  Since we
           // were somehow lucky enough for gets to not overwrite the argument that we
           // passed to it and since the C compiler doesn't think it has anything new to
           // pass to puts it doesn't change any registers' values or do too much to the
           // stack.  This leaves us in the situation where puts is called with the stack
           // and registers set up in the same way as they would be if it were passed the
           // address of i, just the same as gets.

   // The gets call with the stack variable's address (so an address high on the stack)
   // could have left main's return address intact, but also could have overwritten it
   // with garbage.  Garbage as main's return address would likely result in a jump to
   // a random location (probably not part of your program) and cause the OS to kill the
   // program (possibly with an unhandled SIGSEGV) which may have looked to you like a
   // normal exit.  Since puts appended a '\n' to the string it wrote and stdout is
   // line buffered by default it would have been flushed before returning from puts
   // even if the program did not terminate properly. 
}

答案 1 :(得分:3)

那是因为您刚刚使用正确的参数调用gets(),并且对puts()的调用发现堆栈未更改。在具有许多寄存器的CPU上,除非gets()不使用包含第一个参数的寄存器,否则这可能会中断。在启用优化的情况下进行编译,这可能就足够了。

如果你在两者之间进行任何函数调用,它也会中断。

使用相同数量代码的干净方法是:

puts(gets(&i));

答案 2 :(得分:2)

当你说“编译并运行正常”时,你的意思是(a)你忽略编译器警告和(b)代码出现以“运行正常”。您的编译器应该生成多个警告,例如

ub.c:2: warning: return type defaults to ‘int’
ub.c: In function ‘main’:
ub.c:3: warning: implicit declaration of function ‘gets’
ub.c:4: warning: implicit declaration of function ‘puts’
ub.c:5: warning: control reaches end of non-void function

此外,如果您在多个平台上尝试此操作,您会发现它并不总是“运行正常” - 它可能会打印垃圾和/或崩溃。

答案 3 :(得分:0)

您的gets(&i)函数实际上是获取字符串。 puts()对您声明两个语句的顺序没有影响。

答案 4 :(得分:0)

通过堆栈魔术,它并不一定适用于每台机器和实现。 puts();只是意味着你不传递任何参数,但是某些东西在堆栈上,它是指向字符串的指针(确实偶然); puts接受它(它不知道你在堆栈上没有任何东西,只是“相信”你做了)并且它是否有效。由于要由调用者来清理堆栈,所以一切都很顺利(如果它本来是被调用者的任务,那就会出现问题)。它起作用的事实是“机会”,(可能发生,但你不能相信太多);它编译的事实是由标准或编译器决定警告但不停止编译(可能你可以添加严格遵守特定标准的选项,然后代码可能无法编译)

相关问题