`
ppgunjack
  • 浏览: 81168 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

c/c++中静态链接过程中对未被代码引用的符号的检查

阅读更多
看到一个应该关注的知识点。
之前看到的一个帖子:
http://my.unix-center.net/~Simon_fu/?p=263
大致情况讲的都比较清楚了
a.h:
/*******************************************/

#include <string> 

using namespace std; 

class A 
{ 
public: 
    A(void); 
    A(const char *s); 
    ~A(void); 
private: 
    string name; 
};
/**********************************************/
a.cpp:
/**********************************************/
#include "a.h" 
#include <iostream> 
using namespace std; 
A::A(void) 
{ 
    cout<< "A construct!\n"; 
} 
A::A(const char *s) 
{ 
    name = string(s); 
    cout<< "A " + name + " construct!\n"; 
} 
A::~A(void) 
{} 
static A g_a("globalA");
/************************************************/

假设上述代码编译成一个静态库lib.a,该静态库中声明了一个全局变量,预期加载该静态库的时候该全局变量会被创建。
另外创建一个测试的工程来加载这个静态库:

test_staticlib.cpp:
/*************************************************/
#include <stdio.h> 
#include "../static_lib/a.h" 
int main() 
{ 
    //A a; 
    //A aa("Local A"); 
    getchar(); 
}


这个代码在输出时是不会创建全局变量g_a,这个很好理解,因为虽然test_staticlib.cpp include了a.h,但是代码链接时并没发现需要链接a.h中声明的符号,因此实际不会链接那个静态库。
当代码为:
int main() 
{ 
    A a; 
    //A aa("Local A"); 
    getchar(); 
}

则可以发现静态变量g_a会被创建,这里应该思考一下,为何在声明"A a;"之后,静态变量g_a会被创建,而之前的代码却不会?
原因在于代码test_staticlib.cpp引入了A a,这导静态链接时致链接程序需要在参数路径下的目标文件或者lib中找到A的代码块进行链接,当在发现A定义位于lib.a之中时,它会同时将lib.a中所有定义的符号找到对应的代码块。
所以第一段代码虽然include了a.h,但实际没有出现过g_a的符号,所以即使include了,也不会创建变量g_a。
而第二段代码由于引入了符号a需要链接lib.a中定义的对象A的代码块,导致了链接器对lib.a中所有符号的链接检查,发现还存在静态全局变量g_a,于是将g_a也静态链接(拷贝)到执行文件当中。注意这里的“所有”,即使是那些main根本没有引用到的符号,链接程序也会一一查清楚这些符号是否有对应的模块代码存在。
举个例子就比较清楚了:

a.h:
/*******************************************/

#include <string> 

using namespace std; 

class A 
{ 
public: 
    A(void); 
    A(const char *s);
    ~A(void); 
private: 
    string name; 
};
extern int k;                            <-------------------------------------
void fn();                                 <-------------------------------------
/**********************************************/
a.cpp:
/**********************************************/
#include "a.h" 
#include <iostream> 
using namespace std; 
A::A(void) 
{ 
    cout<< "A construct!\n"; 
} 
A::A(const char *s) 
{ 
    name = string(s); 
    cout<< "A " + name + " construct!\n"; 
} 
A::~A(void) 
{} 
static A g_a("globalA");
void fn(){k++;}                              <-------------------------------------
/************************************************/

生成静态库的代码添加箭头部分代码。
该静态库可以成功生成,因为k是作为外部定义的变量,其符号不会在链接器生成lib时去找对应的定义k的代码块链接。
但此时再重新编译链接test_staticlib.cpp会发现会报k缺少引用的错误:
undefined reference to `k'
可以看到尽管test_staticlib.cpp的main函数代码中完全没使用到fn(),但由于对lib.a的链接不仅仅链接了A代码模块,也顺带查找了fn中k符号的链接位置,导致编译器报错。
所以这牵扯到一个问题:如果你的代码中的符号需要链接lib.a中的某段代码,那么你也要同时确定这个lib.a中所有出现的其他符号能顺利找到对应的链接代码块,这种确定性检查会级联到所有和它直接或间接关联的库。

原作者的文章说gcc和ms的链接策略不一样,他在gcc中即使main中没有a.cpp中定义的符号,只包含a.h也能创建g_a,但在我机器上mingw4.5.2上gcc和vs表现是一样的。

static A g_a("globalA")这个定义只在lib.a中存在,因此如果main函数没任何符号会需要跨模块访问其他obj或者lib文件,是没有道理去查看lib.a,并且能意识到应该链接static A g_a("globalA")的符号定义的。


分享到:
评论

相关推荐

    C/C++笔试题

    在 C++ 中,函数名可能由于重载等原因被修饰,导致在链接阶段无法找到 C 函数对应的符号。使用 `extern "C"` 可以避免这种名称修饰,使 C++ 代码能够与 C 代码正确链接。 #### 13. `switch` 语句中的注意事项 `...

    [面试/笔试系列1]经典C/C++面试题

    这种情况可能是因为定义未包含在链接过程中,或者是拼写错误。解决方法包括确保所有必要的源文件都已包含在项目中,检查头文件和库文件是否正确配置。 - **C2005 错误** 通常是由于重复定义了同一个符号。这可能是...

    C与C++程序编译链接全过程解析.rar

    本文将深入探讨C与C++程序的编译链接全过程,帮助你理解这两个语言背后的机制。 首先,我们从C语言的编译全过程开始。C语言是一种静态类型的语言,这意味着在程序运行前必须进行编译。编译过程主要包括以下几个步骤...

    static,extern,全局变量的引用(c_c++)

    4. 运行结果表明,`test.c` 文件中的`i`变量被成功调用,并且修改了其值,而`fu1.c` 和 `fu2.c` 文件中的`i`变量并未被修改。 #### 七、总结 通过对`static`、`extern`以及全局变量引用的介绍和分析,我们可以清楚...

    C/C++笔试题(附答案,华为面试题系列)

    答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern "C"修饰的变 量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调 用C 函数。C++提供了一个C 连接交换指定符号...

    Visual Unit(C/C++单元测试工具)3.0 简明教程

    - 当引用的头文件未被正确找到时会出现此错误。解决方案是检查头文件的路径是否正确,或者添加头文件的搜索路径。 - **3.2 编译错误** - **3.2.1 编译错误:文件未找到** - 当源文件或头文件路径不正确时会出现该...

    Visual C++ 编译链接信息手册

    《Visual C++ 编译链接信息手册》是针对C++编程者的一份极其重要的参考资料,尤其对于在使用Visual C++开发环境中遇到编译和链接问题的开发者来说,它提供了宝贵的解决方案。手册涵盖了一系列关于编译器选项、链接器...

    C++编译器GCC的源代码

    5. **链接器接口**:GCC与链接器的交互,如如何处理外部符号引用,以及如何生成可重定位或可执行的目标文件。 6. **库支持**:在处理C++源代码时,GCC需要理解和使用C++标准库,包括STL(Standard Template Library)...

    如何解决 error LNK2019 无法解析的外部符号,该符号在函数 中被引用

    错误 LNK2019 是一个常见的链接器错误,在Visual Studio等C++开发环境中经常会遇到。这个错误通常意味着编译器在链接阶段找不到某个函数或变量的定义,即在对象文件或库中没有对应的实现。这通常是由于以下原因引起...

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

     李文娟,中国石油大学(华东)硕士,现供职于国家行政学院,工作后一直从事软件开发和软件项目管理工作,对计算机语言、计算机体系结构、操作系统都非常熟悉,尤其是精通C和C++编程技术. 目录 封面 -19 封底 -18 扉页...

    嵌入式C_C++语言精华.pdf

    这对于C++程序中包含C库的情况尤为重要,因为它确保了C++代码能正确地引用C代码中的符号。 **应用场景**: - **C库的调用**:当C++程序需要调用C语言编写的库时,需要使用 `extern "C"` 来避免名称修饰(name ...

    win764位系统下VC++2010中设置FFTW时遇到的若干问题及解决办法

    当尝试编译包含FFTW函数调用的代码时,可能会遇到链接器错误LNK2019,该错误通常表示找不到对某个符号的引用。例如: ``` error LNK2019: unresolved external symbol _fftw_plan_dft_1d@16 referenced in function...

    vc报错解决办法 error LNK2019无法解析的外部符号,该符号在函数 中被引用

    这个错误是链接器(Linker)发出的,因为它在已编译的对象文件或库中找不到在源代码中引用的特定函数或变量的实现。现在让我们深入了解一下这个问题及其解决方案。 首先,错误消息中的“外部符号”指的是在编译时...

    C/C++程序编译流程详解

    C/C++程序的编译流程是软件开发中的基础步骤,对于理解程序的构建至关重要。整个流程分为四个主要阶段:预处理、编译、汇编和链接。以下是对这些阶段的详细解析: 1. 预处理(Preprocessing) 预处理阶段是编译的第...

    C++常见错误汇总

    (一般是汉字或中文标点符号或全角空格),这种情况大部分都是从别的地方直接把代码赋值到编译器中导致的。解决方法是检查代码是否存在非法字符。 error C2057: expected constant expression 这个错误是希望是...

    c++语言程序设计教程答案

    - **解析**: 编译过程中可能会发现语法错误或者类型不匹配等问题,而在链接阶段也可能遇到问题,比如未定义的符号或引用。 - **9. 编译C++源程序时,出现了警告错误(Warning)也可以生成可执行文件。** (√) - ...

    《链接器和加载器》(中文版)

    - 弱外部符号是一种特殊符号类型,可以在链接过程中被覆盖。 **维护调试信息**: - 编译器和链接器需要保留足够的信息以便于调试。 **实际的问题**: - 讨论了符号管理在实际应用中可能遇到的问题及解决方案。 ##...

    Visual C++编译器常用选项设置

    - **Pointer to Member Representation**: 设置类定义与引用之间的关系,默认为 `Best-Case Always`,即假设在引用类之前该类已经被定义。 - **Enable Exception Handling**: 启用异常处理机制。 - **Enable Run-...

    谈谈连接器是如何工作的

    【连接器工作原理详解】 ...总结起来,连接器在链接过程中处理强弱符号,确保程序中不会有重复定义的强符号,同时通过解析静态库解决符号引用。理解这些原理有助于开发者诊断和解决与`lianker`和连接器相关的问题。

Global site tag (gtag.js) - Google Analytics