end*_*ith 6 c external inline-functions
我正在尝试用内联函数替换一些宏子程序,因此编译器可以优化它们,因此调试器可以进入它们,等等.如果我将它们定义为普通函数,它可以工作:
void do_something(void)
{
blah;
}
void main(void)
{
do_something();
}
Run Code Online (Sandbox Code Playgroud)
但如果我将它们定义为内联:
inline void do_something(void)
{
blah;
}
void main(void)
{
do_something();
}
Run Code Online (Sandbox Code Playgroud)
它说"错误:未定义的外部".那是什么意思?在黑暗中刺了一下,我试过了
static inline void do_something(void)
{
blah;
}
void main(void)
{
do_something();
}
Run Code Online (Sandbox Code Playgroud)
没有更多的错误.函数定义和对函数的调用都在同一个.c文件中.
有人可以解释为什么一个有效,另一个没有?
(第二个相关问题:如果我想在多个.c文件中使用它们,我在哪里放入内联函数?)
Tim*_*fer 10
首先,编译器并不总是内联标记为的函数inline; 例如,如果您关闭所有优化,它可能不会内联它们.
定义内联函数时
inline void do_something(void)
{
blah
}
Run Code Online (Sandbox Code Playgroud)
并且使用该函数,即使在同一个文件中,对该函数的调用也是由链接器而不是编译器解决的,因为它是隐含的"外部".但是这个定义本身并没有提供函数的外部定义.
如果您包含声明而没有 inline
void do_something(void);
Run Code Online (Sandbox Code Playgroud)
在可以看到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在其声明中使用关键字,如下所示:Run Code Online (Sandbox Code Playgroud)static inline int inc (int *a) { return (*a)++; }如果要编写要包含在ISO C90程序中的头文件,请编写
__inline__而不是inline.在两个重要的情况下,三种类型的内联行为类似:当在函数
inline上使用关键字时static,如上面的示例,以及在不使用inline关键字的情况下首次声明函数 ,然后定义inline,如下所示:Run Code Online (Sandbox Code Playgroud)extern int inc (int *a); inline int inc (int *a) { return (*a)++; }在这两种常见情况下
inline,除了速度之外,程序的行为与未使用关键字的行为相同.当一个函数是内联的
static,如果函数的所有调用都集成到调用者中,并且函数的地址从未使用过,那么函数自己的汇编代码永远不会被引用.在这种情况下,除非您指定选项,否则GCC实际上不会为函数输出汇编代码-fkeep-inline-functions.由于各种原因,某些调用无法集成(特别是,无法集成函数定义之前的调用,也无法在定义中进行递归调用).如果存在非集成调用,则该函数将像往常一样编译为汇编代码.如果程序引用其地址,则必须像往常一样编译该函数,因为无法内联.请注意,函数定义中的某些用法可能使其不适合内联替换.这些用法包括:使用varargs,使用alloca,使用可变大小的数据类型,使用计算goto,使用非本地goto和嵌套函数.
-Winline当标记的函数inline无法替换时,使用将发出警告,并将给出失败的原因.根据ISO C++的要求,GCC认为在类的主体内定义的成员函数被标记为内联,即使它们未使用
inline关键字显式声明.你可以用它来覆盖它-fno-default-inline.除非您
always_inline为函数指定属性,否则GCC不会在未优化时内联任何函数,如下所示:Run Code Online (Sandbox Code Playgroud)/* Prototype. */ inline void foo (const char) __attribute__((always_inline));本节的其余部分特定于GNU C90内联.
当内联函数不是时
static,编译器必须假定可能存在来自其他源文件的调用; 由于全局符号只能在任何程序中定义一次,因此不能在其他源文件中定义该函数,因此无法集成其中的调用.因此,非static内联函数总是以通常的方式自行编译.如果在函数定义中同时指定
inline和extern,则该定义仅用于内联.在任何情况下,函数都不会自行编译,即使您明确地引用其地址也是如此.这样的地址成为外部引用,就像您只声明了该函数一样,并且没有定义它.这种组合
inline和extern几乎具有宏的效果.使用它的方法是将函数定义放在带有这些关键字的头文件中,并将定义的另一个副本(缺少inline和extern)放在库文件中.头文件中的定义将导致对函数的大多数调用进行内联.如果函数的任何使用仍然存在,则它们将引用库中的单个副本.