在C/C++中,函数定义存在于头文件中还是仅存在于声明中?以math.h中的函数pow()为例。如果函数的定义没有出现在头文件中,那么头文件包含什么,以便函数定义被正确链接?
math.h
pow()
acruukt91#
简短回答:* 看情况 *长长的回答:* 看情况 *第一个问题:什么是函数定义?*在C++中,一个函数既可以 * 声明 *,也可以 * 定义 *。一个声明的函数看起来像这样:
// <cmath> namespace std { double pow(double base, double exp); }
字符串而一个定义的看起来像这样:
#include <cmath> double std::pow(double base, double exp) { // Do stuff... }
型不同之处在于,定义的函数具有一个代码块(由大括号{...}表示)。因此,声明和定义之间的区别在于,定义以函数的底层代码为特征,而声明则没有任何代码。但是假设它只声明了,编译器怎么知道函数的位置呢?通常,C是在不同的 * 翻译单元 * 中编译的(阅读:每个翻译单元包括一个公共头文件(这里是<cmath>),它提供了它的函数的声明(这里是::std::pow)。其中一个单元还包含::std::pow的 definition。当这些单元-以对象文件或库(单元的集合)的形式-由 * 链接器 * 放置在一起时,函数的所有使用都将调用位于定义它的单元中的适当代码。如果定义函数的单元没有被链接,链接器将报错。请注意,只能有一个定义。如果多个单元有自己的::std::pow定义,链接器将再次由于多个定义而抱怨。然而,这个“一个定义规则”并不普遍,它的例外是:1.inline 函数(由inline存储类说明符表示),可以在多个单元中定义,但最终会丢弃除一个定义外的所有定义。这还包括 constexpr 和consteval(immediate)函数,这两个函数都是隐式内联的。1.static 函数(由static存储类说明符表示),其位于其转换单元的本地;它们不与来自其它单元的符号冲突-即使它们共享相同的标识符。但::std::pow的情况实际上相当复杂。通常,它会被外部定义。然而,从C26开始,它必须是一个 constexpr 函数,所有这些函数都是内联的 * 根据定义 *(参见cppreference)。标准库中的许多其他函数也是如此。它们中的许多是在外部定义的(在另一个翻译单元中,如::std::exit),但也有许多是在各自的头文件中定义的(如::std::is_constant_evaluated)。在其他情况下,外部函数在后来的标准版本中被内联(如::std::pow)。总之,::std::pow可能在<cmath>头中定义…而它也可能不是。这取决于标准的实施和版本。但一般情况是,是的,函数是在声明它们的头文件之外定义的(在另一个翻译单元中)。
{...}
<cmath>
::std::pow
inline
static
::std::exit
::std::is_constant_evaluated
rks48beu2#
头文件只是由预处理器包含在编译单元中的源文件。从技术上讲,它们可以包含任何C或C代码,但在实践中,它们用于公开编译单元之间的定义/声明,同时隐藏实现细节。在C语言中,同一个外部对象/函数不能有两个定义,所以头文件通常只包含函数声明或变量定义。在C中,有一个类似的规则:一个定义规则。但是也有一些例外,比如模板定义、类成员定义和带有inline说明符的函数。但是它们的定义必须在所有翻译单元中匹配,否则行为是未定义的。在pow的情况下,程序可以访问它,因为编译器默认链接libc。
pow
2条答案
按热度按时间acruukt91#
简短回答:* 看情况 *
长长的回答:* 看情况 *
第一个问题:什么是函数定义?*
在C++中,一个函数既可以 * 声明 *,也可以 * 定义 *。
一个声明的函数看起来像这样:
字符串
而一个定义的看起来像这样:
型
不同之处在于,定义的函数具有一个代码块(由大括号
{...}
表示)。因此,声明和定义之间的区别在于,定义以函数的底层代码为特征,而声明则没有任何代码。但是假设它只声明了,编译器怎么知道函数的位置呢?
通常,C是在不同的 * 翻译单元 * 中编译的(阅读:每个翻译单元包括一个公共头文件(这里是
<cmath>
),它提供了它的函数的声明(这里是::std::pow
)。其中一个单元还包含
::std::pow
的 definition。当这些单元-以对象文件或库(单元的集合)的形式-由 * 链接器 * 放置在一起时,函数的所有使用都将调用位于定义它的单元中的适当代码。如果定义函数的单元没有被链接,链接器将报错。请注意,只能有一个定义。如果多个单元有自己的
::std::pow
定义,链接器将再次由于多个定义而抱怨。然而,这个“一个定义规则”并不普遍,它的例外是:
1.inline 函数(由
inline
存储类说明符表示),可以在多个单元中定义,但最终会丢弃除一个定义外的所有定义。这还包括 constexpr 和consteval(immediate)函数,这两个函数都是隐式内联的。1.static 函数(由
static
存储类说明符表示),其位于其转换单元的本地;它们不与来自其它单元的符号冲突-即使它们共享相同的标识符。但
::std::pow
的情况实际上相当复杂。通常,它会被外部定义。然而,从C26开始,它必须是一个 constexpr 函数,所有这些函数都是内联的 * 根据定义 *(参见cppreference)。标准库中的许多其他函数也是如此。它们中的许多是在外部定义的(在另一个翻译单元中,如
::std::exit
),但也有许多是在各自的头文件中定义的(如::std::is_constant_evaluated
)。在其他情况下,外部函数在后来的标准版本中被内联(如::std::pow
)。总之,
::std::pow
可能在<cmath>
头中定义…而它也可能不是。这取决于标准的实施和版本。但一般情况是,是的,函数是在声明它们的头文件之外定义的(在另一个翻译单元中)。
rks48beu2#
头文件只是由预处理器包含在编译单元中的源文件。从技术上讲,它们可以包含任何C或C代码,但在实践中,它们用于公开编译单元之间的定义/声明,同时隐藏实现细节。
在C语言中,同一个外部对象/函数不能有两个定义,所以头文件通常只包含函数声明或变量定义。
在C中,有一个类似的规则:一个定义规则。但是也有一些例外,比如模板定义、类成员定义和带有
inline
说明符的函数。但是它们的定义必须在所有翻译单元中匹配,否则行为是未定义的。在
pow
的情况下,程序可以访问它,因为编译器默认链接libc。