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

new*_*erl 113 c inline

IMO只使函数具有翻译单元的范围.

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

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

lit*_*adv 98

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*/};
Run Code Online (Sandbox Code Playgroud)

这个紧密循环将在每次迭代时执行函数调用,并且函数内容实际上明显小于编译器执行调用所需的代码.inline本质上将指示编译器将上面的代码转换为等效的:

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

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

显然,这是一个显示要点的示例,而不是真正的代码.

static是指范围.在C中,它表示函数/变量只能在同一个翻译单元中使用.

  • @VoidStar实际上`静态`(有或没有'内联')可以很好地在标题中,没有理由不这样做.模板适用于C++,这个问题是关于C. (12认同)
  • 同样重要的是要注意声明为内联的代码属于标题,而正常(非模板)源代码不能进入标题而不会导致多个重定义错误.因此,即使在内联声明内容时,即使编译器选择不内联它,仍然存在标准强制的多重重新定义,以避免出现的行为. (8认同)
  • 实际上,`inline`不会_instruct_编译器进行任何内联尝试。它仅允许程序员在不违反ODR的情况下将功能主体包含在多个转换单元中。这样做的副作用是,当编译器_would_内联函数时,实际上可以做到这一点。 (4认同)
  • 不,"静态"是指范围.在C中,它表示函数/变量只能在同一个翻译单元中使用. (3认同)
  • @littleadv:将函数定义放在头文件中的主要原因是使它们无法使用,因此将它们显式标记为"inline"是好的样式,imo (2认同)
  • 你真的试过把你的例子编译成汇编吗?至少当我用 GCC 4.9 尝试它时,没有 `-O` 标志,或者如果指定了 `-O0`,函数调用仍然存在。如果用`-O1`或更高版本编译它,现在它实际上是内联的,但即使不指定`inline`仍然内联它,因此`static`和`static inline`没有区别。 (2认同)
  • @Ruslan,这对我很有用,因为它帮助我真正理解你的意思,并发现一个思考的角度,从这个角度你在其中使用的措辞自然也是我可能使用的措辞。您最初的评论提出了一个很好的观点,详细阐述了答案,但其措辞对我来说意味着其他内容 - 我认为这些后续评论将有助于像我这样的其他读者澄清这一点。 (2认同)

Chr*_*oph 80

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

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

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

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

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

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

inlineregister但是,并没有用,因为当程序员编写会使优化成为不可能的代码时,他们会指示编译器抛出错误:外部inline定义不能引用具有内部链接的标识符(因为这些将在不同的翻译单元中不可用)或定义具有静态存储持续时间的可修改局部变量(因为这些变量不会共享翻译单元的状态),并且您不能获取 - register限定变量的地址.

就个人而言,我用的是约定来标记static头部也中函数定义inline,为把函数定义在头文件的主要理由是让他们可以内联.

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

我从来没有写过inline与存储类不同的函数static.

  • 这是正确的答案。关于“ inline”的任何回答,好像实际上是应用于内联一样,都是误导性的,并且可以说是不正确的。没有现代的编译器使用它作为内联或要求它来启用函数内联的提示。 (5认同)
  • 我读了你的整个答案,但我仍然不明白“静态”和“静态内联”之间的语义差异。它们都使定义对其他翻译单元不可见。那么编写“static inline”而不是“static”的合理理由是什么? (3认同)
  • +1 非常有洞察力的提醒,它们并非无用,因为它们指示编译器将不可能进行这些优化视为错误! (3认同)
  • @user541686 最大和最重要的语义差异是“static inline”表达了您对其内联的意图/批准,而“static”则不然。为了明确定义,“语义”基本上是“意义”的一个花哨的词,因此这是最本质的语义差异。因为源代码首先是描述您打算让代码做什么。如果您主动打算进行内联,您应该这么说,而“inline”是 C 语言中最“原生”的表达方式。 (2认同)

ony*_*ony 18

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

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

不幸的是,我找不到任何有关该逻辑的证据.即使从GCC文档中我也无法得出结论,inline禁止使用未使用的功能警告.我很感激,如果有人会分享这个描述的链接.


she*_*ngy 6

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

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

编辑:

编译单元应该是翻译单元

  • 或用花哨的话说:它具有内部联系。 (2认同)
  • 我相信“编译单元”是我写错的东西,没有这样的东西,实际的术语是“翻译单元” (2认同)

R..*_*R.. 5

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