Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么我们需要在C中使用函数之前声明它们?_C_C99 - Fatal编程技术网

为什么我们需要在C中使用函数之前声明它们?

为什么我们需要在C中使用函数之前声明它们?,c,c99,C,C99,在C99中,如果函数在声明之前被调用,编译器将发出警告。例如,这将导致警告: int sum(const int k) { return accsum(k, 0); } int accsum(const int k, const int acc) { if (k == 0) { return acc; } else { return accsum(k-1, k + acc); } } int main() { int x = sum(3); retu

在C99中,如果函数在声明之前被调用,编译器将发出警告。例如,这将导致警告:

int sum(const int k) {
  return accsum(k, 0);
}


int accsum(const int k, const int acc) {
  if (k == 0) {
    return acc;
  } else {
    return accsum(k-1, k + acc);
  }
}

int main() {
  int x = sum(3);
  return 0;
}

我在谷歌搜索后看到的答案是需要声明,以便编译器可以检查参数类型以避免错误。但是,当从sum中调用accsum时,为什么编译器不能在执行accsum之前找到accsum的函数定义呢?

这只是cicra 1970 C的一个遗留问题

强制程序员在使用函数之前声明函数允许编译器一次工作(即只读取一次代码)

40年前,限制编译时间很重要。今天,这只是一个烦恼

编辑:

当然,您必须声明外部函数原型,否则编译器无法知道需要哪些参数和返回值。这与申报顺序无关

安全和方便的做法是将未声明的外部函数视为错误,并将程序员从同一编译单元中的前向声明原型中除掉。 至于允许使用非类型化函数,这是大约1970年的另一个遗留问题。C99不再允许隐式声明,但仍然可以使用K&R样式的声明(没有参数规范的原型)


我认为这种危险的可能性是出于向上兼容的原因而保留的。

实际上,在C中使用函数之前不需要声明函数。如果遇到调用函数的尝试,编译器将假定一个变量参数列表,并且函数返回int

现代编译器在看到声明之前尝试调用函数时发出警告的原因是声明允许编译器检查参数是否为预期类型。给定一个声明,如果参数与函数声明中的规范不匹配,编译器可以发出警告或错误。这捕获了程序员大量的错误


至于编译器为什么不提前寻找定义,有几个因素。首先,C有一个单独的编译模型——不能保证在当前编译单元(即源文件)中会找到函数定义。第二,它允许编译器一次完成工作,从而提高了性能。第三,它确实鼓励程序员实际声明函数(例如在头文件中),这允许与单独的编译模型重用。第四,它增加了编译器在内存资源有限的机器上实际运行的机会。

因为规范说我们需要这样做,否则编译器将不知道如何生成调用函数的代码。我肯定我以前在什么地方见过这个问题,但是SO搜索并不能帮我找到复制品。@iharob你能详细说明一下吗?编译器是如何生成调用函数的代码的?我想你会发现关于转发声明的这一点对@JeffreyBosboom很有启发,但我不会说它是重复的,这是错误的,一点也不麻烦。它不仅仅是一个时间,它还消耗内存。对于小型独立的程序,这可能是恼人的,但是对于大型项目,特别是C++,这对于减少时间和内存消耗非常重要。现在资源非常便宜,但它们仅限于您当前拥有的资源。@loshadvtapkah内存消耗是多少?您将要使用的符号表?@loshadvtapkah它无论如何都需要该表,唯一的区别是它必须在开始时在额外的过程中准备,而不是在解析过程中构建。它使用的所有内存可能都是优化过程,而不是简单的符号表。我同意这种观点,程序员们变得懒惰,不再思考资源的使用。C++的问题是C,而不是C++。与处理模板所需的大量CPU和内存相比,单次编译的收益微不足道。至于@ChronoKitsune评论,它与远期申报无关。当然,您必须声明函数原型。问题是:顺序是什么?既然问题被标记为C和C99,我就回答了这两个问题。我将编辑我的答案,让它更清楚。基本上,这里有两点(你可以复制它们,可能是为了增加它们的权重):效率和非类型化函数。我觉得两者都无关紧要。当然,如果您想在WiFi手表上编译GnuChess,单次编译将是一个救命稻草:)。至于“鼓励”人们声明功能,这是大约1970年的另一个遗留问题。安全的做法是将未声明的函数作为错误处理,但我认为这是出于向上兼容的原因而忽略的。我并不认为我列出的要点彼此无关,正如我建议给出完整的答案一样。函数声明(从能够在声明中指定参数类型的意义上讲)不是1970年遗留下来的-它们是在1989年ANSI(后来的1990年ISO)标准中引入的。在任何情况下,C的设计理念都权衡了安全性,以实现各种形式的效率。今天,你可能会认为这些事情无关紧要,但这并不能改变C以这种方式做事的原因。当然,原型设计来自ANSI C。非类型化函数是1970年遗留下来的。至于效率,我真的看不出保留K&R原型有多“高效”,除了允许在不进行清理的情况下编译老式代码。再说一次,现在的编译器速度是一个没有意义的问题。事实上,第一段是错误的。C编程语言,即ISO/IEC 9899:2011(以及问题中提到的早已过时的9899:1999),不允许imp