我正在遵循学习curses的指南,以及原型中的所有C代码在main()
之前运行,然后定义它们。在我的C ++学习中,我听说过函数原型,但从未完成它,据我所知,它对代码的编译方式没有太大区别。这是程序员的个人选择吗?如果是这样,为什么它包含在C中呢?
答案 0 :(得分:12)
函数原型开发最初没有包含在C中。当你调用一个函数时,编译器只是接受了它的存在,并且它采用了你提供的参数类型。如果您的参数顺序,数字或类型错误,那么太糟糕了 - 您的代码在运行时可能会以神秘的方式失败。
更高版本的C添加了功能原型以解决这些问题。在某些情况下,您的参数会隐式转换为声明的类型,或者标记为与原型不兼容,编译器可能会将错误的顺序和类型数标记为错误。这具有启用varargs函数和它们所需的特殊参数处理的副作用。
请注意,在C中(与C ++不同),声明为foo_t func()
的函数不与声明为foo_t func(void)
的函数相同。后者的原型是没有参数。前者声明了一个没有原型的函数。
答案 1 :(得分:3)
在C中需要进行原型设计,以便你的程序知道你有一个名为x()
的函数,当你还没有定义它时,y()
知道存在{{1} x()
}}。 C做了自上而下的编译,因此需要先定义才能得到简短的答案。
x();
y();
main(){
}
y(){
x();
}
x(){
...
more code ...
maybe even y();
}
答案 2 :(得分:1)
我的印象是,客户可以访问.h文件库并查看可用的功能,而无需查看实现(将在另一个文件中)。
用于查看函数返回的内容/参数。
答案 3 :(得分:1)
函数原型设计是编译器编写的早期遗留物。过去认为编译器必须对源文件进行多次传递才能编译它,效率非常低。
在C中,在某些上下文中,以一种方式引用函数在语法上等同于引用变量:考虑使用指向函数的指针而不是指向变量的指针。在编译器的中间表示中,两者在语义上是不同的,但从语法上讲,无法确定标识符是变量,函数名称还是无效标识符。
由于无法从上下文中确定,如果没有函数原型,编译器每次编译时都需要对每个源文件进行额外的传递。这将为任何编译添加额外的O(n)因子(即,如果编译为O(m),它现在将为O(m * n)),其中n是项目中的文件数。在大型项目中,编译已经达到数小时的顺序,因此非常不希望使用双向编译器。
前向声明所有函数将允许编译器在扫描文件时构建函数表,并能够确定何时遇到标识符,无论它是引用函数还是变量。
因此,C(以及扩展,C ++)编译器在编译时非常有效。
答案 4 :(得分:0)
它允许您有一种情况,即您可以在包含父容器类的单独.h文件中定义迭代器类。由于你已经在迭代器中包含了父头,你不能拥有像“getIterator()”这样的方法,因为返回类型必须是迭代器类,因此它需要在迭代器中包含迭代器头。父头创建包含的循环循环(一个包括另一个包括其自身,包括另一个,等等)。
如果将iterator类原型放在父容器中,则可以使用这样的方法而不包含迭代器头。它只能起作用,因为你只是说这样的对象存在并将被定义。
有一些方法可以解决它,比如有一个预编译的标题,但在我看来它不那么优雅,并带来一系列的缺点。值得注意的是,这是C ++,而不是C.但是,在实践中,您可能会遇到这样一种情况:您希望以这种方式安排代码,而不是类。