为什么将C函数声明为静态内联?

iam*_*mer 23 c objective-c

我遇到了一个声明为C函数的例子:

static inline CGPoint SOCGPointAdd(const CGPoint a, const CGPoint b) {
    return CGPointMake(a.x + b.x, a.y + b.y);
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,我在.h文件中声明了实用程序C函数并在.m文件中实现它们,就像这样:

CGPoint SOCGPointAdd(const CGPoint a, const CGPoint b) {
    return CGPointMake(a.x + b.x, a.y + b.y);
}
Run Code Online (Sandbox Code Playgroud)

我可以在任何我想要的地方"内联"使用这个函数,它也应该是"静态的",因为它不与任何对象相关联,比如Objective-c方法.指定"静态"和"内联"的重点是什么?

Eri*_*hil 53

inline并不意味着您可以使用"内联"功能(在其他功能中使用功能是正常的;您不需要这样做inline); 它鼓励编译器将函数构建到使用它的代码中(通常以提高执行速度为目标).

static表示函数名称未外部链接.如果未声明该函数static,则需要编译器使其在外部可见,以便它可以与其他对象模块链接.为此,编译器必须包含函数的单独非内联实例.通过声明该函数static,您允许在当前模块中内联它的所有实例,可能不会留下单独的实例.

static inline通常使用在调用例程中比使用调用机制更好的小函数,只是因为它们如此短而快,实际上它们比调用单独的副本更好.例如:

static inline double square(double x) { return x*x; }
Run Code Online (Sandbox Code Playgroud)

  • ... 到不同模块中的同一个对象。具有内部链接的标识符仅指代一个模块内的对象。如果在不同的模块中使用相同的标识符,则它指的是不同的对象;它们没有链接到引用同一个对象。(顺便说一下,我在这里所说的模块是 C 标准中所指的 **翻译单元**。)具有内部链接的标识符可以在一个模块内的多个函数中使用以引用同一个对象。然后是没有链接的标识符。它们只在声明它们的块内引用一个对象。 (5认同)
  • 由于历史原因,链接标识符的规则有点复杂。粗略地说,在文件范围内声明的标识符默认具有外部链接。添加 `static` 会将它们更改为内部链接。默认情况下,在块作用域(函数内部)声明的标识符没有链接。添加 `static` 也会将它们更改为内部链接。这是一个复杂的问题:添加 `extern` 而是将块范围内的声明更改为引用先前可见的声明,而不是没有链接。之前可见的声明可能有 `static`,导致…… (5认同)
  • A**模块**是一个编译单元:一个源文件加上它包含的所有头文件.分别编译多个模块时,必须将它们链接到一个程序中.使用`static`的方式部分来自语言的历史.如果我们今天从头开始重新定义它,我们可能会将其称为"内部".涉及的概念称为**连接**.C中的标识符可以有三种类型的链接:外部,内部和无.具有外部链接的标识符可以在多个模块中使用,并且,因为它们是链接的,所以标识符将引用... (4认同)
  • ... 标记为“extern”的声明引用具有内部链接的标识符。最重要的是,`static` 不仅改变了在块中声明的标识符的链接,它还改变了它们对象的存储类(当对象被创建和销毁时)。 (4认同)
  • 更正:在块范围内用 `static` 声明的标识符仍然没有链接,而不是内部链接。只有在文件范围内,“static”才会传递内部链接。 (3认同)
  • 如果没有明确请求外部链接,`inline` 是否意味着 C 中的内部链接(与 C++ 相对)?6.7.4p7 说的是“内联定义不提供函数的外部定义”,但我不确定我是否理解正确。 (2认同)

Lew*_*sey 6

在C中,inline意味着它是内联定义。它没有内部联系,没有联系。它永远不会到达链接器,这意味着如果编译器不使用该内联定义来内联对编译单元中函数的每个引用,那么如果具有相同名称的符号(C 使用具有外部链接的未损坏的标识符)不会由编译中的另一个翻译单元导出。编译器对函数引用的实际内联完全由优化标志或__attribute__((always_inline))

static inline和之间没有区别static,两者都不内联函数,并在 -O0 上的汇编输出中提供函数作为链接器的内部链接符号,并且都内联并优化了在汇编输出中包含该函数-O1。static inline确实有一个怪癖,您可以在它之前使用非静态内联原型,除非该原型被忽略并且不用作前向声明(但在静态函数之前使用非静态原型是一个错误)。

  • inline-std=gnu90 / gnu89(GCC <5.0,默认使用)/ extern inline(GCC 5.0 及以上,使用-std=gnu11):这是编译器仅内联定义。此内联定义不会发生外部可见的函数发射(在使用汇编器和链接器的汇编输出中)。如果文件中对函数的所有引用实际上并未由编译器内联(并且内联发生在更高的优化级别或使用__attribute__((always_inline)) inline float func()),则如果编译器不向链接器发出外部定义,则会出现本地链接器错误(并且如果另一个翻译单元未导出具有外部链接的同名符号)。这允许单独定义同一符号的内联定义和外联函数,一个是内联的,另一个是外联的,但不能在同一个翻译单元中,因为编译器会混淆它们,并且超出范围的定义将被视为重新定义错误。内联定义仅对编译器可见,并且每个翻译单元都可以有自己的。内联定义无法导出到其他文件,因为内联定义未到达链接阶段。为了在编译时实现这一点,内联定义可以位于头文件中并包含在每个翻译单元中。这意味着 inline 的使用是一个编译器指令,而 extern/static 是指为链接器生成的外联版本。如果该函数未在翻译单元中定义,则无法内联它,因为它留给链接器。如果函数已定义但未内联,则编译器决定内联时将使用此版本

  • extern inline(GCC <5.0) / inline(GCC >5.0):为此内联定义发出外部可见函数,无论它是否内联,这意味着该说明符只能在其中一个翻译单元中使用。这直观上与“外部”相反

  • static inline:本地可见的外联函数由编译器发出到汇编输出,并带有用于此编译器内联定义的汇编器的本地指令,但如果所有函数都能够内联,则可以在更高的优化级别上进行优化;它永远不会允许产生链接器错误。它的行为与 相同,static因为编译器将static在更高的优化级别上内联定义,就像static inline.

  • inline不存在的函数不应static包含非常量静态存储持续时间变量或访问静态文件范围变量,这将产生编译器警告。这是因为如果外联版本是从不同的翻译单元提供的,则函数的内联和外联版本将具有不同的静态变量。编译器可能内联某些函数,不发出要链接到这些引用的本地符号,并将链接保留给链接器,链接器可能会找到外部函数符号,该外部函数符号被假定为相同的函数,因为它具有相同的标识符。所以它提醒程序员,它在逻辑上应该是const,因为修改和读取static会导致未定义的行为;如果编译器内联此函数引用,它将读取函数中的新静态值,而不是在先前调用该函数时写入的静态值,其中先前对该函数的引用是未内联的,因此该变量上次调用中写入的内容可能是由另一个翻译单位提供的。在这种情况下,它会产生每个翻译单元的本地副本和全局副本,并且未定义正在访问哪个副本。使其成为 const 可确保所有副本都是相同的,并且彼此之间永远不会改变,从而使行为得到定义和了解。

  • 在非内联定义之前/之后使用inline/extern inline原型意味着原型被忽略。

  • 在内联定义之前使用inline原型是如何在没有副作用的情况下原型化内联函数的方法,在内联定义之后声明内联原型不会改变任何内容,除非存储说明符发生变化。

  • extern inline//在内联定义之前/之后使用extern常规原型与定义相同extern inline;它是使用内联定义提供函数的外部外联定义的提示。

  • 在文件中没有定义的原型上使用extern inline/ ,但在文件中引用它会导致被忽略,然后它的行为就像常规原型(/ 常规,它们是相同的)inlineinlineextern

  • 在文件中没有定义的原型上使用static inline/static但在文件中引用它会导致正确的链接和正确的类型使用,但编译器警告说尚未定义具有内部链接的函数(因此它使用外部定义)

  • extern// 在or定义之前使用常规extern inline原型会出现“‘func’的静态声明遵循非静态声明’错误static inlinestatic之后使用它不会执行任何操作,并且它们会被忽略。允许在定义之前/之后使用static或原型。在定义之前使用原型将被忽略,并且不会充当前向声明。这是与定义导致错误之前的常规原型不同的唯一方式,但事实并非如此。static inlinestatic inlineinlinestatic inlinestatic inlinestaticstatic

  • static inline在常规 // / / 定义之前使用原型externstatic导致覆盖说明符,并与前向声明一样正确地起作用static inlineextern inlinestatic inline

  • __attribute__((always_inline))始终将函数符号内联到翻译单元中,并使用此定义。该属性只能用于定义。存储/内联说明符不受此影响并且可以与其一起使用。


Mer*_*ede 3

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

通过声明函数inline,您可以指示编译器将该函数的代码集成到其调用者的代码中(将该函数的完整代码直接替换到调用它的位置)。这通过消除函数调用开销而使执行速度更快。这就是为什么内联函数应该非常短。

  • 我对 Obj-C 有点生疏,但我认为 C 静态与 Obj-C 类和静态(类)方法没有太大关系,特别是因为这是一个函数而不是方法? (3认同)