由内联函数引发的unresolved external symbol及inline诠释

本文探讨了内联函数的运行机制及其在类实例化中的应用,详细解释了如何避免未解决的外部符号错误,并提供了正确的代码示例。

一、运行机制及优点

内联函数的运行机制是在调用处展开函数体所有代码。由于中间过程省去了内存寻址/堆栈操作等费时的系统开销,显然,内联化的函数将可显著提高普通函数所执行效率的N个数量级。特别是在需要大量使用的场合中,是最佳化实施手段之一。

二、类和内联函数

通常我们抽像一个类的步骤如下:

1、将需要抽像的类的声明(declaration)放在一个独立的*.h中;

2、将需要抽像的类的定义(definition)放在一个独立的*.cpp中(用cl.exe编译成一个独立的*.obj),由于该类是一个我们工程中很普通的成员,所以我们将它所有的成员函数内联化

然后,当我们工程中的某个地方需要调用时,就在它源码(must be a *.cpp file)内的顶部以#include <*.h>方法将该类的*.h包含进来,以此declared info.来识别我们需要用到的symbol(标识符)。接着,我们就可以使用该类的类型。

三、类的实例化及将引发的unresolved external symbol错误及因和果

举例如下,假设我们已编写了一个String类,它的声明放在“String.h”,定义在“String.cpp”,然后我们在主程序文件“test.cpp”中调用:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

#include <iostream>

#include "String.h"

int main()

{

String job( "Programmer" ), name( "dreamerate" );

cout << job << ": " << name << endl;

}

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

编译以cl.exe的命令行方式进行如下步骤:

cl /nologo /ML /GX /O2 /c string.cpp

cl /nologo /ML /GX /O2 /c test.cpp

结果,出现如下错误:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

test.obj : error LNK2001: unresolved external symbol "public: __thiscall String:
:~String(void)" (??1String@@QAE@XZ)
test.obj : error LNK2001: unresolved external symbol "public: char & __thiscall
String::operator[](int)" (??AString@@QAEAADH@Z)
test.obj : error LNK2001: unresolved external symbol "public: bool __thiscall St
ring::operator==(char const *)" (??8String@@QAE_NPBD@Z)
test.obj : error LNK2001: unresolved external symbol "public: bool __thiscall St
ring::operator==(class String const &)" (??8String@@QAE_NABV0@@Z)
test.obj : error LNK2001: unresolved external symbol "class ostream & __cdecl op
erator<<(class ostream &,class String const &)" (??6@YAAAVostream@@AAV0@ABVStrin
g@@@Z)
test.obj : error LNK2001: unresolved external symbol "class istream & __cdecl op
erator>>(class istream &,class String &)" (??5@YAAAVistream@@AAV0@AAVString@@@Z)

test.obj : error LNK2001: unresolved external symbol "public: __thiscall String:
:String(char const *)" (??0String@@QAE@PBD@Z)
test.obj : error LNK2001: unresolved external symbol "public: __thiscall String:
:String(void)" (??0String@@QAE@XZ)
String.exe : fatal error LNK1120: 8 unresolved externals

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

问题原因如下:由内联函数机制所知,我们都知道,它是在调用处展开函数体,由于这个展开过程是在cl.exe编译时进行的,又由于我们将调用处及类的文件分别单独编译,所以……将引发以上问题。

四、总结

内联函数只限出现在所编译依赖的目标文件(object file)中。所以当以inline来修饰一个在外部定义的类成员函数(class member functions)时,必须在调用处的源码中以#include方法加引进来。^_^

附 最后补上纠正代码如下:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

#include <iostream>

#include "String.cpp"  // #include "String.h" 改为#include "String.cpp"

int main()

{

String job( "Programmer" ), name( "dreamerate" );

cout << job << ": " << name << endl;

}

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

编译以cl.exe的命令行方式进行如下步骤:

cl /nologo /ML /GX /O2 /c test.cpp

 
### `static inline` 函数在 C 语言中的使用与常见报错分析 在 C 语言中,`static inline` 是一种常见的函数定义方式,尤其在嵌入式系统和内核编程中被广泛使用。它结合了 `static` 和 `inline` 的语义特性:`static` 限制函数的作用域为当前翻译单元,避免符号冲突;`inline` 建议编译器将函数体直接插入调用点,以减少函数调用开销。 然而,在实际使用过程中,可能会遇到以下典型错误: #### 报错类型一:多重定义(Multiple Definition) 当一个 `static inline` 函数在多个源文件中被包含并定义时,可能引发链接阶段的多重定义错误。由于 `static` 已经限制了函数作用域为当前翻译单元,因此通常不会出现此问题。但在某些编译器实现中,若头文件未正确保护或函数被外部可见的宏展开触发,则仍可能出现重复定义问题[^1]。 解决方案是确保此类函数仅在头文件中定义一次,并且头文件应使用保护宏防止重复包含: ```c // utils.h #ifndef UTILS_H #define UTILS_H static inline int add(int a, int b) { return a + b; } #endif // UTILS_H ``` #### 报错类型二:无法解析的外部符号(Unresolved External Symbol) 如果在某个翻译单元中声明了一个非 `static` 的 `inline` 函数但未提供定义,或者试图从其他翻译单元引用一个 `static inline` 函数,会导致链接失败。`static inline` 函数不能被其他翻译单元访问,因为其作用域限定于当前文件。 例如: ```c // a.c #include "utils.h" int main() { int result = add(2, 3); // 正确:add 是 static inline,在 a.c 中可见 return 0; } ``` ```c // b.c extern int add(int a, int b); // 错误:add 是 static,无法跨翻译单元访问 int test() { return add(4, 5); } ``` 此时编译器会报告 `undefined reference to 'add'` 错误。解决方法是移除对 `static inline` 函数的外部引用,或将函数定义改为非 `static` 或者通过宏控制其可见性[^1]。 #### 报错类型三:内联失败(Inlining Failed) 即使使用了 `inline` 关键字,编译器也可能因优化级别不足或函数过于复杂而拒绝内联。此时,函数仍然作为普通函数处理,可能导致额外的函数调用开销。可通过查看编译器警告信息或生成的汇编代码确认是否成功内联。 建议在关键路径上使用 `static inline` 函数,并配合 `-O2` 或更高优化等级以提高内联效率。 --- ### 示例:正确的 `static inline` 使用方式 ```c // math_utils.h #ifndef MATH_UTILS_H #define MATH_UTILS_H static inline int square(int x) { return x * x; } #endif // MATH_UTILS_H ``` ```c // main.c #include "math_utils.h" #include <stdio.h> int main() { printf("Square of 5 is %d\n", square(5)); // 调用内联函数 return 0; } ``` --- ### 总结 - `static inline` 函数适用于小型、频繁调用且不需要跨翻译单元访问的函数。 - 确保头文件使用保护宏,避免重复定义。 - 不要尝试从其他翻译单元引用 `static inline` 函数。 - 编译器可能忽略 `inline` 建议,需关注优化设置和编译器输出。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值