• 关于C++编译时内链接和外链接


     

    最近在阅读《大规模C++ 程序设计》 在第1部分,作者讨论了内链接和外链接问题(因为大规模的C++程序有繁多的类和单元。因此编译速度是个大问题)

    这里记录一下关于内链接和外链接的理解。

     

    • C++的编译过程

       

      我们以bcb 和 vs 为例,一个程序文档一般都由 .cpp 文件 和 .h文件构成。但编译时,只有.cpp 参与编译。.h文件则会被预编译器复制到引用他的.cpp中。

      然后,.cpp文件被编译成.obj文件。

      接着,通过链接器,将obj文件链接为exe文件。

       

    • 链接过程
    1. 链接过程中,很重要的步骤,就是判断是否有重复定义。如果有重复定义,则判定为链接失败无法编译完成。

       

    2. 另一个重要步骤是,引用外部的定义。比如,在 a.cpp中定义了一个全局变量。b.cpp如果引用了这个全局变量。

      当经过编译分别得到a.obj 和 b.obj, 链接器发现b.obj引用了一个全局变量。则要去其他的.obj文件中搜索这个变量的定义。

      如果没有搜索到,则会报未定义的外部符号。然后无法编译完成。

     

    • 外部链接

       

      所谓外部链接,就是被外部引用的符号。这么说太抽象。下面是一个具体的例子:

       

      用vs建立一个VC++的控制台工程。

    1. 在stdafx.h中添加

    #include <iostream>

    using namespace std;

     

     

    1. 添加 a.cpp 和 b.cpp 源文件。
    2. a.cpp代码

    #include "stdafx.h"

     

    int nA = 100;

    1. b.cpp代码

    #include "stdafx.h"

     

    extern int nA;

     

    void printnA()

    {

        cout<<nA<<endl;

    }

    1. 编译代码。

      完全正常,那么链接器在链接b.obj的时候是如何知道这个nA 在哪里定义的呢?

      其本质,是因为我们指定了extern int nA 告诉链接器,这个变量是从外部引用的。

      链接器说:好,我去找找。于是就找到了a.obj。这就是外部链接过程。

    2. 修改b.cpp

    #include "stdafx.h"

     

    //extern int nA;

    int nA = 200;

     

    void printnA()

    {

        cout<<nA<<endl;

    }

     

    编译时,链接器则会输出错误

    错误    1    error LNK2005: "int nA" (?nA@@3HA) 已经在 a.obj 中定义    D: externtestexterntest.obj    externtest

    错误    2    error LNK1169: 找到一个或多个多重定义的符号    D:externtestDebugexterntest.exe    1    1    externtest

     

    这又是为什么呢? b.cpp 和 a.cpp 中nA 定义冲突了。

     

    其本质原因是,在cpp中定义的外部变量,都是要参与外链接的。前面说了链接器会检查有没有重定义。

    他现在a.obj中发现了 int nA, 接着又在 b.obj中发现了int Na,重复的外部符号。因此就报错了。

     

    • 内部链接
    1. 现在,修改b.cpp

    #include "stdafx.h"

     

    //extern int nA;

    //int nA = 100;

    static int nA = 100;

     

    void printnA()

    {

        cout<<nA<<endl;

    }

     

    我们将int nA 声明为静态的。编译链接就完全没有问题。为什么呢?

    因为,静态的定义,都是内部链接。因此,链接并不会去检查重复。

    (当然,如果在一个.cpp中定义了2个同名的静态变量,编译阶段就通不过,此时是编译器校验而不是链接器校验)

     

    1. 再修改b.cpp

    #include "stdafx.h"

     

    //extern int nA;

    //int nA = 100;

    extern int nA;

    static int nA = 100;

     

    void printnA()

    {

        cout<<"nA in b.cpp"<<nA<<endl;

    }

     

    再修改a.cpp

    #include "stdafx.h"

     

    int nA = 300;

     

    void PrintnA2()

    {

        cout<<"nA in a.cpp"<<nA<<endl;

    }

     

    修改main函数

    void printnA();

    void PrintnA2();

     

    int _tmain(int argc, _TCHAR* argv[])

    {

        printnA();

        PrintnA2();

        return 0;

    }

     

    得到这样的结果

     

     

    这时的情况是:b.cpp中外部引用了一个nA,还定义了一个静态内部Na.

    但并不冲突,事实情况是,在b.cpp中凡是引用nA的地方,都引用的内部nA,既static 声明的nA

    在a.cpp中,则依然是引用的全局的Na.

     

    我们可以看到,c++ 对于同时存在内部和外部同名符号时,只认内部符号。

    • 函数的内,外链接
    1. 其实我们通过上面第8个例子,已经看到了。函数默认是外部链接的。而且可以不加extern 关键字。但是加了extern关键字也不错。

      我们修改main.cpp 用extern声明完全没有问题。

       

     

    #include "stdafx.h"

     

    void printnA();

    void PrintnA2();

    //void printnA3();

    extern void printnA3();

     

    int _tmain(int argc, _TCHAR* argv[])

    {

        printnA();

        PrintnA2();

        printnA3();

        return 0;

    }

     

     

    这说明,函数也是外部链接的。只要声明了函数,链接器就去其他的obj中查找外部引用。

    1. 我们修改b.cpp,加入一个静态的printnA4

    #include "stdafx.h"

     

    //extern int nA;

    //int nA = 100;

    extern int nA;

    static int nA = 100;

     

    void printnA()

    {

        cout<<"nA in b.cpp"<<nA<<endl;

    }

     

    void printnA3()

    {

        cout<<"::nA in b.cpp"<<::nA<<endl;

    }

     

    static

    void printnA4()

    {

        cout<<"static printnA4 in b.cpp"<<::nA<<endl;    

    }

     

    接着修改 main.cpp

     

    #include "stdafx.h"

     

    void printnA();

    void PrintnA2();

    //void printnA3();

    extern void printnA3();

    void printnA4();

     

    int _tmain(int argc, _TCHAR* argv[])

    {

        printnA();

        PrintnA2();

        printnA3();

        printnA4();

        return 0;

    }

     

        编译,我们会得到一个报错

        

     

    当我们把 b.cpp中的static注释,又会恢复正常。

     

    和静态定义的变量一样。静态的函数也是内链接的。并不会被外链接访问(可以这么理解,当声明一个函数为static,就不想让外部(其他的obj)访问他)

     

    1. 继续修改b.Cpp,添加一个inline函数 printnA5 (为排除干扰,将10例子中的static注释掉)

      请注意 ::nA 这个写法,在b.cpp中并不会因为写了::nA 就访问去全局的nA了

    #include "stdafx.h"

     

    //extern int nA;

    //int nA = 100;

    extern int nA;

    static int nA = 100;

     

    void printnA()

    {

        cout<<"nA in b.cpp"<<nA<<endl;

    }

     

    void printnA3()

    {

        cout<<"::nA in b.cpp"<<::nA<<endl;

    }

     

    //static

    void printnA4()

    {

        cout<<"static printnA4 in b.cpp"<<::nA<<endl;    

    }

     

    inline void printnA5()

    {

        cout<<"inline printnA5 in b.cpp"<<::nA<<endl;

    }

     

     

    同样,在main.cpp中去引用printnA5

     

    #include "stdafx.h"

     

    void printnA();

    void PrintnA2();

    //void printnA3();

    extern void printnA3();

    void printnA4();

    void printnA5();

     

    int _tmain(int argc, _TCHAR* argv[])

    {

        printnA();

        PrintnA2();

        printnA3();

        printnA4();

        printnA5();

        return 0;

    }

     

    同样得到链接错误

     

    这说明内联函数,也是无法参与外链接的。

    • 总结

       

      我们现在没有涉及类。仅仅涉及了C中的一些特性。但已经知道了什么内链接和外链接。

       

      我的理解是:外链接就是要让连接器去其他的obj中查找符号的引用行为。内链接则是不需要链接器去其他obj查找的引用行为。

       

       

      会发生外链接的行为(涉及到类之后,会有更多情况,这里只是一部分)

       

      • 引用了其他cpp中定义的全局变量。
      • 引用了其他cpp中定义的 非静态,非内联函数。

     

    仅内链接的行为

    • 静态的变量
    • 静态函数
    • 内联函数

     

     

  • 相关阅读:
    XML 加密、解密
    word 转 pdf
    PowerDesigner(PowerDesigner15.1.0.2850)下载、安装以及破解
    SQL 2005 18456
    SQL 2005端口的修改
    SQL——统计查询
    由于启动用户实例的进程时出错,导致无法生成 SQL Server 的用户实例 解决办法
    ASP.NET MVC中Model View Controller的开发顺序
    使用Windows身份验证的Intranet网站安全管理 Windows Authentication
    ASP.NET web.config中数据库连接字符串connectionStrings节的配置
  • 原文地址:https://www.cnblogs.com/songr/p/5222768.html
Copyright © 2020-2023  润新知