C 内联函数和“未定义的外部”函数错误

发布于 2025-01-08 06:38:04 字数 581 浏览 0 评论 0原文

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

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 文件中使用内联函数,我应该将它们放在哪里?)

I'm trying to replace some macro subroutines with inline functions, so the compiler can optimize them, so the debugger can step into them, etc. If I define them as normal functions it works:

void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

but if I define them as inline:

inline void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

it says "Error: Undefined external". What does that mean? Taking a stab in the dark, I tried

static inline void do_something(void)
{
  blah;
}

void main(void)
{
  do_something();
}

and no more errors. The function definition and call to the function are in the same .c file.

Can someone explain why one works and the other doesn't?

(Second related question: Where do I put inline functions if I want to use them in more than one .c file?)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦幻之岛 2025-01-15 06:38:04

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

当您定义内联函数

inline void do_something(void)
{
  blah
}

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

如果您在可以看到 inline 定义的 C 文件中包含不带 inline 的声明

void do_something(void);

,则编译器将提供该函数的外部定义,并且错误应该消失。

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

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

另一种选择是在标头中将其定义为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 关键字
声明,如下所示:

 静态内联 int
 公司(整数*a)
 {
   返回(*a)++;
 }

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

三种类型的内联在两种重要情况下的行为类似:
inline 关键字用于 static 函数时,例如
上面的例子,当一个函数第一次声明时不使用
inline 关键字,然后用 inline 定义,如下所示:

 extern int inc (int *a);
 内联整型
 公司(整数*a)
 {
   返回(*a)++;
 }

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

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

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

根据 ISO C++ 的要求,GCC 考虑内部定义的成员函数
类的主体被标记为内联,即使它们不是内联
使用 inline 关键字显式声明。你可以覆盖这个
-fno-default-inline

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

 /* 原型。 */
 内联 void foo (const char) __attribute__((always_inline));

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

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

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

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

First, the compiler does not always inline functions marked as inline; eg if you turn all optimizations off it will probably not inline them.

When you define an inline function

inline void do_something(void)
{
  blah
}

and use that function, even in the same file, the call to that function is resolved by the linker not the compiler, because it is implicitely "extern". But this definition alone does not provide an external definition of the function.

If you include a declaration without inline

void do_something(void);

in a C file which can see the inline definition, the compiler will provide an external definition of the function, and the error should go away.

The reason static inline works is that it makes the function visible only within that compilatioin unit, and so allows the compiler to resolve the call to the function (and optimize it) and emit the code for the function within that compilation unit. The linker then doesn't have to resolve it, so there is no need for an external definition.

The best place to put inline function is in a header file, and declare them static inline. This removes any need for an external definition, so it resolves the linker problem. However, this causes the compiler to emit the code for the function in every compilation unit that uses it, so could result in code bloat. But since the function is inline, it is probably small anyway, so this usually isn't a problem.

The other option is to define it as extern inline in the header, and in one C file provide and extern declaration without the inline modifier.

The gcc manual explains it thus:

By declaring a function inline, you can direct GCC to make calls to
that function faster. One way GCC can achieve this is to integrate
that function's code into the code for its callers. This makes
execution faster by eliminating the function-call overhead; in
addition, if any of the actual argument values are constant, their
known values may permit simplifications at compile time so that not
all of the inline function's code needs to be included. The effect on
code size is less predictable; object code may be larger or smaller
with function inlining, depending on the particular case. You can
also direct GCC to try to integrate all "simple enough" functions into
their callers with the option -finline-functions.

GCC implements three different semantics of declaring a function
inline. One is available with -std=gnu89 or -fgnu89-inline or
when gnu_inline attribute is present on all inline declarations,
another when -std=c99, -std=c1x, -std=gnu99 or -std=gnu1x
(without -fgnu89-inline), and the third is used when compiling C++.

To declare a function inline, use the inline keyword in its
declaration, like this:

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

If you are writing a header file to be included in ISO C90 programs,
write __inline__ instead of inline.

The three types of inlining behave similarly in two important cases:
when the inline keyword is used on a static function, like the
example above, and when a function is first declared without using the
inline keyword and then is defined with inline, like this:

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

In both of these common cases, the program behaves the same as if you
had not used the inline keyword, except for its speed.

When a function is both inline and static, if all calls to the
function are integrated into the caller, and the function's address is
never used, then the function's own assembler code is never
referenced. In this case, GCC does not actually output assembler code
for the function, unless you specify the option
-fkeep-inline-functions. Some calls cannot be integrated for various
reasons (in particular, calls that precede the function's definition
cannot be integrated, and neither can recursive calls within the
definition). If there is a nonintegrated call, then the function is
compiled to assembler code as usual. The function must also be
compiled as usual if the program refers to its address, because that
can't be inlined.

Note that certain usages in a function definition can make it
unsuitable for inline substitution. Among these usages are: use of
varargs, use of alloca, use of variable sized data types , use of computed goto,
use of nonlocal goto, and nested functions.
Using -Winline will warn when a function marked inline could not
be substituted, and will give the reason for the failure.

As required by ISO C++, GCC considers member functions defined within
the body of a class to be marked inline even if they are not
explicitly declared with the inline keyword. You can override this
with -fno-default-inline.

GCC does not inline any functions when not optimizing unless you
specify the always_inline attribute for the function, like this:

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

The remainder of this section is specific to GNU C90 inlining.

When an inline function is not static, then the compiler must
assume that there may be calls from other source files; since a global
symbol can be defined only once in any program, the function must not
be defined in the other source files, so the calls therein cannot be
integrated. Therefore, a non-static inline function is always
compiled on its own in the usual fashion.

If you specify both inline and extern in the function definition,
then the definition is used only for inlining. In no case is the
function compiled on its own, not even if you refer to its address
explicitly. Such an address becomes an external reference, as if you
had only declared the function, and had not defined it.

This combination of inline and extern has almost the effect of a
macro. The way to use it is to put a function definition in a header
file with these keywords, and put another copy of the definition
(lacking inline and extern) in a library file. The definition in
the header file will cause most calls to the function to be inlined.
If any uses of the function remain, they will refer to the single copy
in the library.

青衫负雪 2025-01-15 06:38:04

对于要与 C99 一起使用的内联函数(它们只进入该语言),您必须在头文件

inline void do_something(void)
{
  blah
}

和一个编译单元(又名 . c) 您放置某种

void do_something(void);

没有内联的“实例化”。

For inline functions to work with C99 (they only came there into the language) you'd have to give the definition in a header file

inline void do_something(void)
{
  blah
}

and in one compilation unit (aka .c) you place some sort of "instantiation"

void do_something(void);

without the inline.

情泪▽动烟 2025-01-15 06:38:04

如果您想从多个文件中使用它们,则必须将它们放入头文件中。

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

You have to put them in a header file if you want to use them from multiple files.

And for the linker error: the default declaration of a function implies that it's "extern", but since it's inlined, the linker can find the compiler-generated symbol stub, hence the error.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文