原文链接:https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
语法
1、function-specifier:
inline
_Noreturn
约束
2 函数说明符应该只能用于函数标识符(函数名)的声明中。
3 具有外部链接的函数的内联定义,不应包含具有静态或线程存储持续时间的可修改对象的定义,也不应包含对具有内部链接的标识符的引用。
static int a = 100;
extern void hello(int b); // 创建了一个外部定义
inline void hello(int b)
{
static int c; // 不应该定义可修改的静态变量c
c = b;
a += b; // 不应该引用具有内部链接的变量a
}
内部链接(internal linkage)和外部链接 (external linkage)是和编译单元(translation unit)相关的一个术语,其主要影响函数或者对象的作用域及存储方式是全局只存储一个,还是全局有许多量的副本。
具有external linkage的标识符可以被其它源文件使用,整个程序内有效,并且全局只有一个。具有internal linkage的标识符只能被本translation unit所引用,如果有多个translation unit,则会有多个副本,即每个cpp文件中都会有一个。
在缺省情况下,常量(const)具有static属性(internal linkage),而非常量(non-const)具有external linkage 属性。
4 在一个主机端环境中,在对main函数的声明中不应该出现任何函数说明符。
语义
5 一个函数说明符可以出现多次;行为就跟只出现一次是一样的。
6 用inline函数说明符声明的一个函数是一个内联函数。将一个函数作为一个内联函数,对那个函数的调用尽可能地快。
1、内联函数在可读性方面与函数是相同的,但是在编译时是将函数直接嵌入调用程序的主体,省去了调用/返回指令,提高了执行效率。
2、内联函数可以调试,而宏定义是不可以调试的。内联函数与宏本质上是两个不同的概念,如果程序编写者对于既要求快速,又要求可读的情况下,则应该将函数冠以inline。
3、内联函数的参数类型由编译器检查,这是对 C 的 #define 宏的一个改进。
7 任一具有内部链接的函数可以是一个内联函数。对于具有外部链接的函数,要应用以下限制:如果一个函数用一个inline函数说明符,那么它也应该被定义在同一个翻译单元。如果一个翻译单元中,一个函数的所有文件作用域内的声明包含了inline函数说明符,且没有extern,那么在那个翻译单元中的定义是一个内联定义。一个内联定义并不提供对该函数的一个外部定义,并且并不禁止在另一个翻译单元提供一个外部定义。一个内联定义提供了对一个外部定义的替代品,这样一个翻译单元可以用来在同一个翻译单元中实现对该函数的任一调用。对该函数的一次调用是否使用内联定义还是使用外部定义是未指定的。[注:由于一个内联定义区别于相应的外部定义,也区别于在其它翻译单元内的任何其他对应内联定义,因此具有静态存储持续时间的所有对应对象在每个定义中也不同。]
一个.cpp文件或者.c文件才称为一个 translation unit 编译单元,头文件(.h)不能称为一个translation unit,因为其最终会被加入到include它们的.cpp/.c文件中去,编译器编译的是.cpp/.c文件,而不是.h文件。
8 用一个_Noreturn函数说明符声明的一个函数不应该返回到其调用者
推荐的实践
9 如果用一个_Noreturn函数说明符声明的一个函数中出现了能够返回到其调用者,那么实现应该对这种情况生成一条诊断消息。
10 例1 具有外部连接的一个内联函数的声明,可以产生一个外部定义,要么是一个仅在翻译单元内可用的定义。带有extern的一个文件作用域声明创建一个外部定义。以下例子展示了一整个翻译单元。
inline double fahr(double t)
{
return (9.0 * t) / 5.0 + 32.0;
}
inline double cels(double t)
{
return (5.0 * (t - 32.0)) / 9.0;
}
extern double fahr(double); // 创建了一个外部定义
double convert(int is_fahr, double temp)
{
/* 一个翻译器可以执行内联替换 */
return is_fahr ? cels(temp) : fahr(temp);
}
11 注意,上述代码,对fahr的定义是一个外部定义,因为fahr用extern声明,但对cels的定义是内联定义。因为,cels具有外部连接且被引用,一个外部定义必须出现在另一个翻译单元中(见6.9);内联定义与外部定义是有所不同的,并且任意一个都可用于函数调用。
12 示例2
Noreturn void f () {
abort(); // ok
}
_Noreturn void g (int i) { // causes undefined behavior if i <= 0
if (i > 0) abort();
}