【问题标题】:C inline functions and "undefined external" errorC 内联函数和“未定义的外部”错误
【发布时间】:2012-03-13 00:45:58
【问题描述】:

我正在尝试用内联函数替换一些宏子例程,以便编译器可以优化它们,以便调试器可以单步执行它们等等。如果我将它们定义为普通函数,它就可以工作:

void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

但如果我将它们定义为内联:

inline void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

它说“错误:未定义的外部”。那是什么意思?在黑暗中刺伤,我尝试了

static inline void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

并且没有更多错误。函数定义和函数调用在同一个 .c 文件中。

谁能解释为什么一个有效而另一个无效?

(第二个相关问题:如果我想在多个 .c 文件中使用内联函数,我应该将它们放在哪里?)

【问题讨论】:

  • 不清楚您将这些定义放在哪里,以及何时收到这些错误。其中一些在标题中吗?您是否在标头中转发声明并尝试从另一个编译单元使用它?
  • 哦,函数定义和函数调用都在同一个.c文件中。
  • 你能发一个sscce吗? (好吧,在这种情况下当然不能编译。)
  • @Mat:不知道你在问什么。我正在定义一个函数,然后在同一个 .c 文件中的主函数中调用它。
  • 这是一个独立的实现?

标签: c external inline-functions


【解决方案1】:

首先,编译器并不总是内联标记为inline的函数;例如,如果您关闭所有优化,它可能不会内联它们。

当你定义一个内联函数时

inline void do_something(void)
{
  blah
}

并使用该函数,即使在同一个文件中,对该函数的调用也由链接器而不是编译器解析,因为它是隐含的“外部”。但仅此定义并不能提供函数的外部定义。

如果您包含不带inline 的声明

void do_something(void);

在可以看到inline定义的C文件中,编译器会提供函数的外部定义,错误应该会消失。

static inline 起作用的原因是它使函数仅在该编译单元内可见,因此允许编译器解析对该函数的调用(并对其进行优化)并在该编译单元内发出该函数的代码.然后链接器不必解析它,因此不需要外部定义。

内联函数最好放在头文件中,并声明它们static inline。这消除了对外部定义的任何需求,因此它解决了链接器问题。但是,这会导致编译器在每个使用它的编译单元中为函数发出代码,因此可能导致代码膨胀。但是由于函数是内联的,所以它可能还是很小的,所以这通常不是问题。

另一种选择是将其定义为标题中的extern inline,并在一个C 文件中提供和extern 声明 不带inline 修饰符。

gcc 手册是这样解释的:

通过声明内联函数,您可以指示 GCC 调用 该功能更快。 GCC 可以实现这一目标的一种方法是集成 将该函数的代码写入其调用者的代码中。这使得 通过消除函数调用开销来加快执行速度;在 此外,如果任何实际参数值是常数,它们的 已知值可能允许在编译时进行简化,这样就不会 需要包含所有内联函数的代码。对的影响 代码大小难以预测;目标代码可能更大或更小 使用函数内联,具体取决于具体情况。你可以 还指示 GCC 尝试将所有“足够简单”的功能集成到 他们的呼叫者使用选项-finline-functions

GCC 实现了声明函数的三种不同语义 排队。 -std=gnu89-fgnu89-inline 或 当gnu_inline 属性出现在所有内联声明中时, 另一个当-std=c99-std=c1x-std=gnu99-std=gnu1x (不带-fgnu89-inline),编译C++时使用第三个。

要声明内联函数,请在其声明中使用 inline 关键字 声明,像这样:

 static inline int
 inc (int *a)
 {
   return (*a)++;
 }

如果您正在编写要包含在 ISO C90 程序中的头文件, 写__inline__ 而不是inline

这三种类型的内联在两种重要情况下表现相似: 当 inline 关键字用于 static 函数时,例如 上面的例子,当一个函数第一次声明时没有使用 inline关键字然后用inline定义,像这样:

 extern int inc (int *a);
 inline int
 inc (int *a)
 {
   return (*a)++;
 }

在这两种常见情况下,程序的行为与您的行为相同 没用过inline关键字,除了它的速度。

当一个函数既是内联函数又是 static 时,如果所有对 函数被集成到调用者中,函数的地址是 从未使用过,则函数自己的汇编代码永远不会 参考。在这种情况下,GCC 并没有真正输出汇编代码 对于函数,除非您指定选项 -fkeep-inline-functions。一些呼叫不能被集成为各种 原因(特别是在函数定义之前的调用 不能被集成,也不能在 定义)。如果存在非集成调用,则函数为 像往常一样编译成汇编代码。该函数还必须是 如果程序引用其地址,则照常编译,因为 不能内联。

请注意,函数定义中的某些用法可以使其 不适合内联替换。这些用法包​​括: 可变参数,alloca 的使用,可变大小数据类型的使用,计算 goto 的使用, 使用非本地 goto 和嵌套函数。 当标记为inline 的函数无法运行时,使用-Winline 会发出警告 被替换,并给出失败的原因。

按照 ISO C++ 的要求,GCC 会考虑定义在 一个类的主体被标记为内联,即使它们不是 使用 inline 关键字显式声明。你可以覆盖这个 -fno-default-inline

GCC 在不优化时不会内联任何函数,除非你 为函数指定always_inline 属性,如下所示:

 /* Prototype.  */
 inline void foo (const char) __attribute__((always_inline));

本节的其余部分特定于 GNU C90 内联。

当内联函数不是static 时,编译器必须 假设可能有来自其他源文件的调用;由于全球 符号在任何程序中只能定义一次,函数不能 在其他源文件中定义,因此其中的调用不能 融合的。因此,非static 内联函数总是 以通常的方式自行编译。

如果在函数定义中同时指定inlineextern, 那么该定义仅用于内联。在任何情况下都不是 函数自己编译,即使你引用它的地址也不行 明确地。这样的地址成为外部引用,就好像您 只声明了函数,并没有定义它。

inlineextern 的这种组合几乎达到了 宏。使用方法是将函数定义放在头文件中 包含这些关键字的文件,并将定义的另一个副本 (缺少inlineextern)在库文件中。中的定义 头文件将导致对函数的大多数调用被内联。 如果该功能的任何用途仍然存在,它们将引用单个副本 在图书馆。

【讨论】:

  • “首先,编译器并不总是内联标记为内联的函数” 是的,但是让编译器决定而不是用宏强制它不是更好吗? “这会导致编译器在每个使用它的编译单元中为函数发出代码”但是如果它在该文件中未使用,它就不会被编译,不是吗?这里有编译后的代码大小限制。
  • “是的,但是让编译器决定而不是用宏强制它不是更好吗?”我不明白这个问题。 “但如果它在那个文件中没有被使用,它就不会被编译,不是吗?”已编译,但未发出机器码;它基本上被“扔掉了”。
【解决方案2】:

要使 inline 函数与 C99 一起使用(它们只在语言中出现),您必须在头文件中给出定义

inline void do_something(void)
{
  blah
}

one 编译单元(又名 .c)中放置某种“实例化”

void do_something(void);

没有inline

【讨论】:

【解决方案3】:

如果你想从多个文件中使用它们,你必须把它们放在一个头文件中。

对于链接器错误:函数的默认声明意味着它是“extern”,但由于它是内联的,因此链接器可以找到编译器生成的符号存根,因此会出现错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 2012-09-17
    • 1970-01-01
    • 2014-03-27
    • 2021-10-28
    • 2016-09-30
    • 1970-01-01
    相关资源
    最近更新 更多