“ static”和“ static inline”函数的区别是什么?

IMO 都使得功能只有一个翻译单元的范围。

“ static”和“ static inline”函数的区别是什么?

为什么 inline应该放在头文件,而不是在 .c文件?

210357 次浏览

inline指示编译器将函数内容嵌入到调用代码中,而不是执行实际调用。

对于频繁调用的小函数来说,可能会带来很大的性能差异。

但是,这只是一个“提示”,编译器可能会忽略它,大多数编译器会尝试“内联”,即使在可能的情况下,作为优化的一部分,没有使用关键字。

例如:

static int Inc(int i) {return i+1};
.... // some code
int i;
.... // some more code
for (i=0; i<999999; i = Inc(i)) {/*do something here*/};

这个紧密循环将在每次迭代中执行一个函数调用,函数内容实际上明显少于编译器执行调用所需的代码。inline实际上将指示编译器将上述代码转换为等效的:

 int i;
....
for (i=0; i<999999; i = i+1) { /* do something here */};

跳过实际的函数调用并返回

显然,这是一个展示这一点的示例,而不是一段真正的代码。

static指的是范围。在 C 语言中,这意味着函数/变量只能在同一个翻译单元中使用。

在 C 语言中,static意味着您定义的函数或变量只能在这个文件中使用(即编译单元)

因此,static inline表示只能在此文件中使用的内联函数。

编辑:

编译单元 应该是 < em > 翻译单元

有一个不同之处不在语言层面,而是在流行的实现层面: 某些版本的 gcc 默认情况下将从输出中删除未引用的 static inline函数,但即使未引用,也将保留纯 static函数。我不确定这适用于哪个版本,但从实用的角度来看,这意味着始终使用 inline作为头中的 static函数可能是一个好主意。

默认情况下,内联定义仅在当前翻译单元中有效。

如果存储类是 extern,则标识符具有外部链接,并且内联定义也提供外部定义。

如果存储类是 static,则标识符具有内部链接,并且内联定义在其他转换单元中是不可见的。

如果未指定存储类,则内联定义仅在当前翻译单元中可见,但标识符仍具有外部链接,并且必须在不同的翻译单元中提供外部定义。如果函数是在当前转换单元内调用的,则编译器可以自由使用内联定义或外部定义。

由于编译器可以内联(而不是内联)任何定义在当前翻译单元中可见的函数(并且,由于链接时间优化,即使在不同的翻译单元中,尽管 C 标准并没有真正考虑到这一点) ,在大多数实际用途中,staticstatic inline函数定义之间没有区别。

inline说明符(类似于 register存储类)只是编译器提示,编译器可以完全忽略它。符合标准的非优化编译器只需要考虑它们的副作用,而优化编译器将在有或没有显式提示的情况下执行这些优化。

不过,inlineregister并非毫无用处,因为它们指示编译器在程序员编写代码时抛出错误,使优化成为不可能: 外部 inline定义不能引用具有内部链接的标识符(因为这些标识符在不同的翻译单元中不可用) ,也不能定义具有静态存储持续时间的可修改局部变量(因为这些变量不会在不同的翻译单元之间共享状态) ,而且你不能获取 register限定变量的地址。

就个人而言,我使用约定在头文件中标记 static函数定义,也标记 inline,因为将函数定义放在头文件中的主要原因是为了使它们不可行。

一般来说,除了头中的 extern声明外,我只使用 static inline函数和 static const对象定义。

我从未编写过使用与 static不同的存储类的 inline函数。

根据我在 GCC 的经验,我知道 staticstatic inline在编译器如何发出关于未使用函数的警告方面有所不同。更准确地说,当你声明 static函数时,如果当前的翻译单元中没有使用它,那么编译器就会产生关于未使用函数的警告,但是你可以通过将它改为 static inline来抑制这种警告。

因此,我倾向于认为 static应该用于翻译单元,并从额外的检查编译器中获益,以发现未使用的函数。而且,应该在头文件中使用 static inline来提供内联函数(由于没有外部链接) ,而无需发出警告。

不幸的是,我找不到这种逻辑的任何证据。即使从 GCC 文档中,我也不能得出 inline抑制未使用的函数警告的结论。如果有人愿意分享描述的链接,我将不胜感激。

在 C + + 中,inline的一个重要作用(我认为在其他答案中还没有提到)是它防止在找到函数的多个定义时出现链接器错误。

考虑一个在头文件中定义的函数,该函数允许它内联到包含头文件的源文件中。如果编译器决定 没有内联(所有对该函数的调用) ,则函数定义将包含在引用它的每个对象文件中(即不内联所有调用)。

这可能会导致函数的多个定义读取链接器(尽管并不总是如此,因为它取决于编译器的内联决策)。如果没有 inline关键字,这将产生一个链接器错误,但是 inline关键字告诉链接器只选择一个定义并放弃其余的定义(这些定义应该是相等的,但是没有选中)。

另一方面,static关键字确保 如果函数包含在对象文件中,它对于该对象文件是私有的。如果多个对象文件包含相同的函数,它们将共存,并且对该函数的所有调用将使用它们自己的版本。这意味着需要占用更多的内存。在实践中,我认为这意味着对头文件中定义的函数使用 static不是一个好主意,最好只使用 inline

在实践中,这也意味着 static函数不能产生连接器错误,因此上述 inline的效果对于 static函数并不真正有用。但是,正如 只有一个答案所建议的那样,添加 inline可能有助于防止对未使用的函数发出警告。

请注意,以上对于 C + + 是正确的。在 C 语言中,inline 有点不一样,您必须在单个源文件中显式地放入一个 extern声明,以便将内联函数发送到该对象文件中,这样它就可以用于任何非内联用途。换句话说,inline意味着一个函数不会被发送到任何源文件中,即使并非所有的调用都是内联的,除非它也被指定为 extern,然后它被发送到 (即使所有的本地调用都是内联的)。但是我不确定它与 static是如何相互作用的。

内嵌式定义没有与 外部ly 链接。

// average.h
#ifndef AVERAGE_H
#define AVERAGE_H
inline double average(double a, double b);
#endif

尝试使用上面的定义从另一个函数调用 内嵌式函数 模块 之后它已经被预处理或链接到一个 c 文件将导致一个错误。

有两种方法可以解决这个问题:

  1. 使它成为 静态内联函数定义。 例如:
// average.h
#ifndef AVERAGE_H
#define AVERAGE_H
static inline double average(double a, double b);
#endif
  1. 包含 c 文件中的定义并使其成为 外部。 例如:
#include "average.h"
extern double average(double a ,double b){
return (a + b) / 2;
}