当我首先没有包含任何头文件时,编译器如何知道sleep函数的原型甚至printf函数?
此外,如果我指定sleep(1,1,"xyz")
或任意数量的参数,编译器仍然编译它.但奇怪的是gcc能够在链接时找到这个函数的定义,我不明白这是怎么可能的,因为实际sleep()
函数只接受一个参数,但是我们的程序提到了三个参数.
/********************************/ int main() { short int i; for(i = 0; i<5; i++) { printf("%d",i);`print("code sample");` sleep(1); } return 0; }
DGentry.. 10
缺少更具体的原型,编译器将假定函数返回int并获取您提供的任意数量的参数.
根据CPU体系结构,参数可以在寄存器中传递(例如,MIPS上的a0到a3),或者像在原始x86调用约定中一样将它们推送到堆栈中.在任何一种情况下,传递额外的参数都是无害的.被调用的函数不会使用传入的寄存器,也不会引用堆栈上的额外参数,但没有什么不好的事情发生.
传递更少的参数更成问题.被调用的函数将使用发生在适当的寄存器或堆栈位置的任何垃圾,并且可能随之发生hijinks.
缺少更具体的原型,编译器将假定函数返回int并获取您提供的任意数量的参数.
根据CPU体系结构,参数可以在寄存器中传递(例如,MIPS上的a0到a3),或者像在原始x86调用约定中一样将它们推送到堆栈中.在任何一种情况下,传递额外的参数都是无害的.被调用的函数不会使用传入的寄存器,也不会引用堆栈上的额外参数,但没有什么不好的事情发生.
传递更少的参数更成问题.被调用的函数将使用发生在适当的寄存器或堆栈位置的任何垃圾,并且可能随之发生hijinks.
在经典C中,您不需要原型来调用函数.编译器将推断该函数返回一个int并获取未知数量的参数.这可能适用于某些体系结构,但如果函数返回int之外的其他内容(如结构)或者有任何参数转换,它将失败.
在您的示例中,可以看到睡眠,编译器会假设原型为
int sleep();
请注意,参数列表为空.在C中,这与void不同.这实际上意味着"未知".如果你正在编写K&R C代码,你可以通过代码来获得未知参数
int sleep(t) int t; { /* do something with t */ }
这一切都很危险,特别是在一些嵌入式芯片上,其中为非原型函数传递参数的方式与原型不同.
注意:链接不需要原型.通常,链接器会自动链接到Linux上的glibc之类的C运行时库.您使用sleep和实现它的代码之间的关联发生在源代码处理很久之后的链接时间.
我建议您使用编译器的功能来要求原型以避免这样的问题.使用GCC,它是-Wstrict-prototypes命令行参数.在CodeWarrior工具中,它是C/C++编译器面板中的"Require Prototypes"标志.