“静态”和“静态内联”功能有什么区别?


123

IMO都使该功能仅具有翻译单元的范围。

“静态”和“静态内联”功能有什么区别?

为什么要inline放在头文件中而不是.c文件中?

Answers:


109

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语言中,这意味着该函数/变量只能在同一翻译单元中使用。


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

8
还需要注意的是,声明为inline的代码属于标头,而通常的(非模板)源代码不能进入标头,而不会引起多个重新定义错误。因此,即使声明内联的东西时,即使编译器选择不内联它,还有一个标准的授权的多重新定义回避行为踢英寸
VoidStar

13
@VoidStar实际上static(带或不带inline)可以很好地放在标题中,看不出为什么没有。模板是用于C ++的,这个问题是关于C的
。– littleadv

2
@littleadv:将函数定义放在头文件中的主要原因是使它们变得不可插入,因此明确标记它们inline是一种好的样式,imo
Christoph

9
实际上inline并不指示编译器进行任何内联尝试。它仅允许程序员在不违反ODR的情况下将功能主体包含在多个转换单元中。这方面的一个副作用是,它是它使编译器,当它内联函数,要真正做到这一点。
Ruslan '18

95

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

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

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

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

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

inline说明符(如register存储类)只有一个编译器暗示,编译器可以自由地完全忽略它。符合标准的非优化编译器仅需遵守其副作用,优化编译器将在有或没有显式提示的情况下进行这些优化。

inline而且register并不是没有用的,因为它们指示程序员在编写使优化无法实现的代码时引发编译器错误:外部inline定义不能引用具有内部链接的标识符(因为这些标识符在其他翻译单元中不可用)或定义具有静态存储持续时间的可修改局部变量(因为它们不会在翻译单元之间共享状态),并且您不能使用- register限定变量的地址。

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

通常,除了使用static inline函数和static const对象定义外,我只使用extern标头中的声明。

我从未用inline存储类不同于编写过函数static


8
这是正确的答案。任何谈论inline好像实际上应用于内联的答案都是误导性的,并且可以说是错误的。没有现代的编译器使用它作为内联或要求它来启用函数内联的提示。
Tyg13

1
赞成“使用约定在内联头文件中标记静态函数定义”。
John Z. Li,

21

根据我在GCC中的经验,我知道这一点,static并且static inline在编译器发出有关未使用功能的警告的方式方面有所不同。更准确地说,当您声明static函数且当前转换单元中未使用该函数时,编译器会生成有关未使用函数的警告,但可以通过将其更改为来禁止该警告static inline

因此,我倾向于认为static应该在翻译单元中使用它,并受益于额外的检查编译器来查找未使用的函数。并且static inline应在头文件中使用,以提供可以内联的功能(由于缺少外部链接)而不会发出警告。

不幸的是,我找不到这种逻辑的任何证据。即使从GCC文档中,我也无法得出结论,inline可以禁止未使用的功能警告。如果有人会共享指向其描述的链接,我将不胜感激。


嗯,还是得到warning: unused function 'function' [clang-diagnostic-unused-function]static inline以建设时功能clang-tidy(V8.0.1),但是以另一种翻译单位使用。但这绝对是结合staticinline!的最佳解释和原因之一。
DrumM

6

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

因此,static inline意味着只能在此文件中使用的内联函数。

编辑:

编译单元应该是翻译单元


2
或用花哨的话说:它具有内部联系。
K-ballo

@AlokSave:编译单元翻译单元之间有区别吗?如果是这样,那么在C ++语言的上下文中哪个更合适?
legends2k 2013年

我相信这the compile unit是我translation unit
写错的

您的答案不完整,因为它主要用于跨翻译单位的头文件中。
DrumM

5

不是在语言级别而是在流行的实现级别上的一个区别:static inline默认情况下,某些版本的gcc 将从输出中删除未引用的函数,但是static即使未引用,它们也会保留纯函数。我不知道这适用于哪个版本,但是从实际情况来看这意味着它可能是一个好主意,始终使用inlinestatic标头的功能。


inline定义使用呢?您是否还暗示不将其用于extern功能?
new_perl 2011年

最新版本的GCC仍然适用吗?如果您举了一个例子并列出了GCC的哪个版本,您的答案将会更加有趣。
Z玻色子2015年

@Zboson:我现在没有这些信息,也没有时间设置和测试很多gcc版本,但是我同意这将是有用的信息。您可以通过查看gcc的历史attribute((used))及其使用来允许asm引用未引用的static函数和数据,从而找到gcc何时开始优化未使用的静态函数/对象。
R .. GitHub停止帮助ICE
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.