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

时间:2014-02-17 17:35:04

标签: objective-c c

我遇到了一个声明为:

的C函数的例子
static inline CGPoint SOCGPointAdd(const CGPoint a, const CGPoint b) {
    return CGPointMake(a.x + b.x, a.y + b.y);
}

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

CGPoint SOCGPointAdd(const CGPoint a, const CGPoint b) {
    return CGPointMake(a.x + b.x, a.y + b.y);
}

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

4 个答案:

答案 0 :(得分:48)

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

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

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

static inline double square(double x) { return x*x; }

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

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

控制

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

  • inline(GCC <5.0,默认使用 -std=gnu90 / gnu89)/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):无论是否内联,都会为此内联定义发出外部可见函数,这意味着此说明符可以只能在其中一个翻译单元中使用。这在直觉上与'extern'相反

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

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

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

  • 在内联定义之前使用 inline 原型是如何在没有副作用的情况下对内联函数进行原型设计,在内联定义之后声明一个内联原型,除非存储说明符发生变化,否则不会发生任何变化。

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

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

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

  • externextern inline 定义之前使用常规 / static inline / static 原型是在非静态声明之后的'func'静态声明' 错误;在什么都不做之后使用它,它们会被忽略。允许在 static 定义之前/之后使用 static inlinestatic inline 原型。在 inline 定义之前使用 static inline 原型会被忽略并且不会充当前向声明。这是 static inlinestatic 定义之前作为常规原型与 static 不同导致错误的唯一方式,但事实并非如此。

  • 在常规 / static inline / extern / static / static inline 定义之前使用 extern inline 原型会导致 static inline 覆盖说明符和作为前向声明的正确行为。

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

答案 3 :(得分:0)

内联函数用于在头文件中定义。小函数在头文件中定义。 它应该是静态的,以便它只能访问静态成员。