函数定义是否存在于头文件中?

Are function definitions present in header files?

提问人:shivansh gautam 提问时间:7/12/2023 最后编辑:Michele Bologninishivansh gautam 更新时间:7/13/2023 访问量:179

问:

在 C/C++ 中,函数定义是存在于头文件中还是仅存在于声明中? 以 中的函数为例。如果头文件中不存在函数的定义,那么头文件包含哪些内容,以便正确链接函数定义?pow()math.h

C++ C 文件声明 定义

评论

0赞 HomeworkHopper 7/12/2023
虽然我假设这个问题指的是 C/C++ 头文件,但没有明确说明。我建议在问题中提及 C 或 C++,并在其中添加它们的标签。
2赞 Jonathan Leffler 7/12/2023
这取决于 — 使用 C++ Boost 库,其中许多是作为“仅标头”代码实现的;它们不需要单独的实现。在 C 语言中定义在标头中定义函数是有意义的。static inline
1赞 john 7/12/2023
问题 1,两者都可以存在,对于定义,函数必须指定为 。问题 2,标题包含一个函数声明,又名函数原型。这确保了函数的正确链接(假设其他所有内容也都是正确的)。听起来像是学校的问题。inline
1赞 George 7/12/2023
对于某些编译器,这不会自动发生,您必须手动链接相应的库。例如,在 gcc 上,您必须添加 -lm 以链接到数学库中。
1赞 Thomas Matthews 7/13/2023
提醒:头文件不要求包含函数定义或声明。

答:

1赞 Dorian 7/12/2023 #1

头文件只是由预处理器包含在编译单元中的源文件。从技术上讲,它们可以包含任何 C 或 C++ 代码,但实际上,它们用于在编译单元之间公开定义/声明,同时隐藏实现细节。

在 C 语言中,不能为同一个外部对象/函数提供两个定义,因此头文件通常只包含函数声明或变量定义。

在 C++ 中,存在类似的规则:一个定义规则。但是很少有例外,例如模板定义、类成员定义和带有说明符的函数。但是它们的定义必须与所有翻译单元匹配,否则行为是未定义的。inline

如果程序可以访问它,因为您的编译器默认链接 libc。pow

3赞 epsiloneridani 7/12/2023 #2

简短的回答:视情况而定。

长答案:这要看情况。


第一:什么是函数定义?

在 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


请注意,只能有一个定义。如果多个单元有自己的定义,链接器会再次抱怨多个定义。::std::pow

但是,这一个定义规则并不通用,它的例外情况是:

  1. 内联函数(由存储类说明符说明符表示),可以在多个单元中定义,但最终将丢弃除一个定义之外的所有定义。这还包括 constexprconsteval(即时)函数,这两个函数都是隐式内联的。inline
  2. 静态函数(由存储类说明符表示),这些函数是其转换单元的本地函数;它们不会与其他单位的符号冲突,即使它们共享相同的标识符。static

但这种情况实际上相当复杂。通常,它会在外部定义。但是,从 C++26 开始,它需要是一个 constexpr 函数,根据定义,所有这些函数也是内联的(参见 cppreference)。::std::pow

标准库中的许多其他函数也是如此。其中许多是在外部定义的(在另一个翻译单元中,例如 ::std::exit),但许多也在它们各自的标头中定义(例如 ::std:is_constant_evaluated)。在其他情况下,外部函数在以后的标准版本中是内联的(如 )。::std::pow


总之,可以在标题中定义...虽然也可能不是。这取决于标准的实施和版本。::std::pow<cmath>

但一般情况是,是的,函数是在声明它们的标头之外定义的(在另一个翻译单元中)。

评论

1赞 Thomas Matthews 7/13/2023
您可能需要讨论静态函数定义或声明。
0赞 epsiloneridani 7/13/2023
@ThomasMatthews 好主意!会的。