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

huangapple go评论78阅读模式
英文:

Are function definitions present in header files?

问题

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

英文:

In C/C++, are function definitions present in header files or only declarations?
Take for example function pow() in math.h. If function's definition is not present in the header file, then what does the header contain so that the function definition is properly linked?

答案1

得分: 3

简短回答:这取决于情况。

详细回答:这取决于情况。


首先:什么是函数定义?

在C++中,函数可以同时被声明定义

已声明的函数如下所示:

// <cmath>

namespace std {
    double pow(double base, double exp);
}

而已定义的函数如下所示:

#include <cmath>

double std::pow(double base, double exp) {
    // 做一些操作...
}

区别在于已定义的函数包含一个代码块(由花括号{...}表示)。因此,声明和定义之间的区别在于定义包含了函数的实际代码,而声明则不包含任何代码。

但是假设只有声明,编译器如何知道函数的位置在哪里?

通常情况下,C++被编译成不同的翻译单元(即文件)。每个翻译单元包括一个公共的头文件(这里是<cmath>),该头文件提供了函数的声明(这里是::std::pow)。

其中一个翻译单元还包含了::std::pow定义。当这些单元(以目标文件或库(单元的集合)的形式)由链接器组合在一起时,所有对函数的使用都将调用位于定义它的单元中的适当代码。如果没有链接定义该函数的单元,链接器将报错。


请注意,只能有一个定义。如果多个单元都有自己的::std::pow定义,链接器会再次报错,因为存在多个定义。

然而,这个一个定义规则并不是普遍适用的,它的例外情况包括:

  1. 内联函数(由inline存储类别指示符指示),可以在多个单元中定义,但最终只保留一个定义。这还包括constexprconsteval(立即执行)函数,它们都是隐式内联的。
  2. 静态函数(由static存储类别指示符指示),它们只存在于它们的翻译单元中;它们不会与其他单元的符号冲突,即使它们具有相同的标识符。

但是::std::pow的情况实际上相当复杂。通常情况下,它应该是在外部定义的。然而,根据C++26的规定,它必须是一个constexpr函数,而按定义所有constexpr函数也都是内联的(请参阅cppreference)。

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


总之,::std::pow可能在<cmath>头文件中被定义,也可能不会。这取决于实现和标准的版本。

但一般情况下,是的,函数的定义通常位于声明它们的头文件之外(在另一个翻译单元中)。

英文:

Short answer: It depends.

Long answer: It depends.


First: What is a function definition?

In C++, a function can be both declared as well as defined.

A declared function looks like this:

// <cmath>

namespace std {
    double pow(double base, double exp);
}

While a defined one looks like this:

#include <cmath>

double std::pow(double base, double exp) {
    // Do stuff...
}

The difference being that the defined function features a code block (denoted by the curly brackets {...}). So the difference between a declaration and a definition is that a definition features the underlying code of the function, whilst a declaration features no code whatsoever.

But assume that it's only declared, how does the compiler know where the function is located at?

Usually, C++ is compiled in different translation units (read: files). Each translation unit includes a common header file (here <cmath>), which provides the declarations of its functions (here ::std::pow).

One of these units also contains the definition for ::std::pow. When these units – either in the form of object files or libraries (a collection of units) – are placed together by the linker, all usage of the function will call the appropriate code located in the unit that defines it. If the unit that defines the function is not linked, the linker will complain with an error.


Do note that there can only be one definition. If multiple units had their own definition of ::std::pow, the linker would again complain due to multiple definitions.

This one definition rule isn't universal, however, and the exceptions to it are:

  1. inline functions (denoted by the inline storage class specifier specifier), which can be defined in multiple units but will end up with all but one definition discarded. This also includes constexpr and consteval (immediate) functions, both of which are implicitly inline.
  2. static functions (denoted by the static storage class specifier), which are local to their translation unit; they do not conflict with symbols from other units – even if they share the same identifier.

But the case of ::std::pow is actually rather complicated. Usually, it would have been defined externally. However, as of C++26, it is required to be a constexpr function, all of which are also inline by definition (see cppreference).

The same goes for many of the other functions in the standard library. Many of them are defined externally (in another translation unit, such as ::std::exit), but many are also defined in their respective header (such as ::std::is_constant_evaluated). And in other cases, external functions are made inline in later standard editions (as is the case of ::std::pow).


In conclusion, ::std::pow might be defined in the <cmath> header... whilst it also might not be. It depends on the implementation and the edition of the standard.

But the general case is that, yes, functions are defined outside the headers that declare them (in another translation unit).

答案2

得分: 1

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

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

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

对于pow的情况,程序可以访问它,因为默认情况下,您的编译器会链接libc。

英文:

Header files are just source files which are included into your compilation unit by the preprocessor. Technically, they can contain any C or C++ code, but in practice, they are used to expose definitions/declarations between compilation units while hiding the implementation details.

In C, you cannot have two definitions for the same external object/function, so header files usually contain only function declarations or variable definitions.

In C++, a similar rule exists: the One Definition Rule. But there are few exceptions such a templates definitions, class member definitions, and function with the inline specifier. But their definitions have to match across all translation unit, otherwise the behavior is undefined.

In the case of pow the program has access to it because your compiler links the libc by default.

huangapple
  • 本文由 发表于 2023年7月12日 20:48:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76670745.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定