动态编译与静态编译

动态编译与静态编译

动态编译

动态编译是某些程式语言在执行时用来增进效能的方法。尽管这技术源于Self,但使用此技术最为人所知的是Java。它可以做一些只在执行时才能得知的最佳化。使用动态编译的执行环境一开始几分钟执行慢,之后,完成大部分的编译和再编译后,会执行快。因为初始效能的延迟,动态编译不适用于一些情况。在许多实作中,一些可以在编译时期做的最佳化被延到执行时期才编译,导致不必要的效能降低。即时编译是一种动态编译的形式。

一个非常近似的技术是递增式编译。递增式编译器用于POP-2POP-11、一些Lisp的版本,如Maclisp和最少一种版本的ML语言(Poplog ML)。这要求编程语言的编译器成为执行环境的一部分。结果,可以在任何时候从终端、从档案、或从执行中程式所建造数据结构中读取源码。然后,转成机器码区块或函数(有可能取代之前同名的函数),之后可立即被程式使用。因为执行中对互动开发和测试的速度的要求,编译后的机器码所做的最佳化程度不如标准“批次编译器”。然而,递增式编译过的程式跑起来通常比同一个程式的解译版本还快。递增式编译因而提供了编译和解译语言优点的混合版。为了移植性,递增式编译通常采用两个步骤。第一个步骤会编译到中间与平台独立的语言,然后再到机器码。在这例子,移植只须改变“后端”编译器。不同于动态编译,递增式编译在程式执行后不会做更进一步的最佳化。

静态编译

静态编译,就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应静态库(.a.lib)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。

动态编译与静态编译的区别

动态编译的可执行文件需要附带一个动态链接库。在执行时,需要调用其对应动态链接库中的命令。所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。缺点一是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行。

静态编译就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应静态库(.a.lib)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。所以其优缺点与动态编译的可执行文件正好互补。

静态编译lib

所谓静态链接就是把函数或过程直接链接到可执行文件中,成为可执行程序中的一部分,当多个程序调用同样的函数时,内存里就会有这个函数的多个拷贝,浪费内存资源。生成lib文件,包含了函数索引以及实现,这个LIB会比较大。

生成和使用静态库

生成静态库

新建项目–win32项目–填写项目名–确定–下一步–应用程序类型:选择静态库。

静态库项目没有main函数,也没有像dll项目中的dllmain

创建项目后添加.h文件,添加相应的导出函数、变量或类,如下所示:

// .h

#ifndef _MYLIB_H_

#define _MYLIB_H_

 

void fun(int a);

 

extern int k;

 

class testclass

{

public:

    testclass();

    void print();

};

 

#endif

 

//.cpp文件如下

#include "stdafx.h"

#include "lib.h"

#include <iostream>

 

void fun(int a)

{

    std::cout<<a<<"lib gen\n";

}

 

int k = 222;

 

testclass::testclass()

{

    std::cout<<"123\n";

}

 

void testclass::print()

{

    std::cout<<"this is testcalss\n";

}//编译工程后就会生成一个.lib文件

使用静态库

需要.h文件,lib文件

1)设置项目属性–vc++目录–库目录为lib所在的路径;

2)将lib添加到项目属性–链接器–输入–附加依赖项(或者直接在源代码中加入#pragma comment(lib, **.lib));

3)在源文件中添加.h头文件。

然后就像平常一样调用普通函数、类、变量,举例如下:

#include <iostream>

#include "lib.h"

 

#pragma comment(lib, "lib.lib")

 

int main()

{

    fun(4);

    std::cout<<k<<std::endl;

    testclass tc;

    tc.print();

    return 0;

}

 

动态编译dll

动态链接则是提供了一个函数的描述信息给可执行文件(并没有内存拷贝),当程序被夹在到内存里开始运行的时候,系统会在底层创建DLL和应用程序之间的连接关系,当执行期间需要调用DLL函数时,系统才会真正根据链接的定位信息去执行DLL中的函数代码。

生成dll文件和lib文件,dll包含实现部分,lib包含索引部分。

Dll的调用过程:在WINDOWS32系统底下,每个进程有自己的32位的线性地址空间,若一个DLL被进程使用,则该DLL首先会被调入WIN32系统的全局堆栈,然后通过内存映射文件方式映射到这个DLL的进程地址空间。若一个DLL被多个进程调用,则每个进程都会接收到该DLL的一个映像,而非多份的拷贝。但,在WIN16系统下,每个进程需要拥有自己的一份DLL空间,可以理解为何静态链接没啥区别。

生成和使用动态库

生成动态库

新建项目–win32项目–填写项目名–确定–下一步–应用程序类型:选择dll–附加选项:选择导出符号–完成。

可以看到生成了一个dllmain.cpp 文件,这是dll应用程序的入口,注意它和普通工程的入口main函数不同,这个文件我们不需要修改。

在这个动态库中我们举例导出一个变量,一个类,一个函数,头文件dll.h如下:

//mydll.h

#ifdef MYDLL_EXPORTS

#define MYDLL_API __declspec(dllexport)

#else

#define MYDLL_API __declspec(dllimport)

#prama comment(lib, "mydll.lib")

#endif

 

//1导出变量,变量在.cpp文件中定义

extern "C" MYDLL_API int ndll;

 

//2导出函数,加extern "C",是为了保证编译时生成的函数名不变,这样动态调用dll时才能

//正确获取函数的地址

extern "C" MYDLL_API int fndll(void);

 

// 3导出类

class MYDLL_API Cdll {

public:

    Cdll(void);

    // TODO: 在此添加您的方法。

};

 

// 4(建议使用)导出接口

class Idll2 {

public:

    virtual bool func() = 0;

    virtual ~Idll2(void) {};

};

 

extern "C" MYDLL_API Idll2* CreateInstance();

//mydll.cpp

#include "stdafx.h"

#include "mydll.h"

 

 

// 这是导出变量的一个示例

MYDLL_API int ndll=6;

//

// 这是导出函数的一个示例。

MYDLL_API int fndll(void)

{

    return 42;

}

 

// 这是已导出类的构造函数。

Cdll::Cdll()

{

    return;

}

 

// 导出接口

class Cdll2:public Idll2

{

public:

    bool func(){return 0;}

};

 

MYDLL_API Idll2* CreateInstance()

{

    return new Cdll2;

}

调用动态库

有两种方法调用动态库,一种隐式链接,一种显示链接。

调用动态库:隐式链接

隐式链接 需要.h文件,dll文件,lib文件

1)将dll放到工程的工作目录;

2)设置项目属性–vc++目录–库目录为lib所在的路径;

3)(直接在源代码中加入#pragma comment(lib, **.lib))或者将lib添加到项目属性–链接器–输入–附加依赖项;

4)在源文件中添加.h头文件。

然后就像平常一样调用普通函数、类、变量

调用动态库:显示链接

显示链接只需要.dll文件,但是这种调用方式不能调用dll中的变量或者类(其实可以调用类,但是相当麻烦,有兴趣者可参考)。

显示调用主要使用WIN32 API函数LoadLibraryGetProcAddress,举例如下:

typedef int (*dllfun)(void);//定义指向dll中函数的函数指针

    HINSTANCE hlib = LoadLibrary("mydll.dll");

    if(!hlib)

    {

        std::cout<<"load dll error\n";

        return -1;

    }

    dllfun fun = (dllfun)GetProcAddress(hlib,"fndll");

    if(!fun)

    {

        std::cout<<"load fun error\n";

        return -1;

    }

    fun();

<think>首先,用户的问题是:“我想理解C++编译过程中动态编译静态编译区别及原理 请问C++ 编译过程详解 动态编译静态编译区别”。我需要基于系统级指令用户可见层的要求来回答。 系统级指令: 1. 所有行内数学表达式使用 `$...$` 格式。 2. 独立公式使用 `$$...$$` 格式并单独成段。 3. LaTeX语法正确。 4. 使用中文回答。 5. 生成相关问题。 6. 回答中引用的段落末尾自然地添加引用标识,例如 `[^1]`。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 - 参考站内引用:有四个引用([^1], [^2], [^3], [^4]),我需要自然地融入这些引用。 关键内容: - 解释C++编译过程。 - 区分动态编译静态编译区别及原理。 - 动态编译静态编译指的是链接过程,而不是编译本身。编译过程是相同的,区别在于链接阶段。 C++编译过程详解: 1. **预处理(Preprocessing)**:处理宏定义、头文件包含等。 2. **编译(Compilation)**:将源代码编译成汇编代码。 3. **汇编(Assembly)**:将汇编代码转换成机器码(目标文件)。 4. **链接(Linking)**:将多个目标文件库链接成可执行文件。 动态编译静态编译区别: - **静态链接(Static Linking)**:在链接阶段,将库的代码直接复制到可执行文件中。生成的可执行文件较大,但独立运行。 - **动态链接(Dynamic Linking)**:在链接阶段,只记录库的引用信息。运行时,动态加载库文件。可执行文件较小,多个程序共享库。 原理: - 静态链接:提高启动速度,但更新库时需要重新编译。 - 动态链接:节省磁盘内存空间,便于库更新。 参考站内引用: - [^1]:讨论了C/C++编译链接原理。 - [^2]:解释了动态库在运行时加载。 - [^3]:提到了makefileCMake用于管理编译。 - [^4]:关于条件编译。 我需要自然地引用这些。例如: - 在解释链接过程时引用[^1]。 - 在讨论动态链接时引用[^2]。 - 在提到编译管理工具时引用[^3]。 - 在条件编译部分引用[^4],但用户问题可能不直接相关,我可以省略或简要提及。 回答结构: 1. **介绍C++编译过程**:逐步解释预处理、编译、汇编、链接。 2. **解释动态链接静态链接**:重点在链接阶段。 3. **区别原理**:比较优缺点。 4. **生成相关问题**:在最后添加相关问题。 生成相关问题: - 基于主题,生成3-5个相关问题,如: 1. 如何选择使用静态链接还是动态链接? 2. 动态链接库(DLL)在WindowsLinux中的实现有何不同? 3. 静态链接库动态链接库的创建方法是什么? 4. 动态链接的运行时加载机制是怎样的? 使用中文回答:整个回答用中文。 数学表达式:用户问题可能不涉及数学,但如果需要,比如在解释原理时,使用行内格式。例如,如果有公式,但我认为这里主要是文本描述。 确保回答真实可靠:基于标准知识,避免错误。 最终回答结构: - 开头简要介绍。 - 详细步骤。 - 区别原理。 - 结尾相关问题。</think>### C++编译过程详解及动态编译静态编译区别 理解C++编译过程是掌握编程基础的关键。编译过程分为四个主要阶段:预处理、编译、汇编链接。动态编译(通常指动态链接)静态编译静态链接)主要在链接阶段有区别,它们影响可执行文件的生成运行方式。下面我将逐步解释整个编译过程,并重点分析动态静态编译区别及原理。 #### 1. **C++编译过程详解** C++编译过程将源代码转换为可执行文件,涉及多个步骤: - **预处理(Preprocessing)**: 预处理是第一步,处理源代码中的宏定义、头文件包含条件编译指令(如 `#include` `#define`)。预处理器生成一个扩展的源文件(通常以 `.ii` 或 `.i` 结尾)。例如: - 头文件包含:将 `#include <iostream>` 替换为实际文件内容。 - 宏展开:将 `#define PI 3.14` 替换为具体值。 - 条件编译:使用 `#ifdef` 或 `#if` 指令,根据条件选择性包含代码块,这有助于平台兼容性(例如,为不同操作系统编写适配代码)[^4]。 输出:一个干净的、无宏的源文件。 - **编译(Compilation)**: 编译器将预处理后的源文件转换为汇编代码(Assembly Code)。这一步进行语法检查、类型检查优化。例如,使用GCC编译器时,命令 `g++ -S main.cpp` 生成 `.s` 文件。 关键点: - 编译器处理高级语言特性(如类、模板),生成低级汇编指令。 - 优化级别可通过参数控制(如 `-O2` 启用中级优化)。 - **汇编(Assembly)**: 汇编器将汇编代码转换为机器码(目标文件,Object File),文件扩展名为 `.o`(Linux)或 `.obj`(Windows)。例如,命令 `g++ -c main.s` 生成 `main.o`。 原理: - 目标文件包含二进制指令符号表(记录变量函数地址)。 - 每个源文件独立汇编,生成一个目标文件。 - **链接(Linking)**: 链接器将多个目标文件库文件合并成单一可执行文件(如 `.exe` 或 `.out`)。这一步解决符号引用(例如,函数调用)。链接分为静态链接动态链接,这是动态编译静态编译的核心区别点[^1]。 过程: - **符号解析**:确保所有函数变量引用都找到定义。 - **重定位**:调整代码地址,使所有部分在内存中正确对齐。 - 工具:GCC的 `g++` 自动调用链接器(`ld`),命令如 `g++ main.o -o program`。 整个编译过程可用公式表示: $$\text{可执行文件} = \text{链接}(\text{汇编}(\text{编译}(\text{预处理}(\text{源代码}))))$$ #### 2. **动态编译静态编译区别及原理** 动态编译静态编译本质上是链接策略的不同,而非独立的编译方式。两者都共享相同的预处理、编译汇编阶段,区别在链接阶段: - **静态编译静态链接)**: - **原理**:在链接阶段,库代码(如标准库或第三方库)被直接复制到最终的可执行文件中。生成的文件是独立的,不依赖外部库。 - 例如:静态库文件( `.a` 或 `.lib`)被嵌入到可执行文件中。 - 优点:可执行文件可独立运行,启动速度快(因为无需运行时加载库)。 - 缺点:文件体积大(库代码被重复复制),更新库时需要重新编译整个程序[^2]。 - **适用场景**:嵌入式系统或需要高移植性的应用(如单文件工具)。 - **动态编译(动态链接)**: - **原理**:在链接阶段,只记录库的引用信息(如函数名称库路径),而不复制库代码。库代码在程序运行时才被加载到内存。 - 例如:动态库文件( `.so` 或 `.dll`)在运行时由操作系统动态加载。 - 优点:可执行文件体积小;多个程序共享同一库实例,节省内存;库更新方便(只需替换库文件,无需重新编译程序)[^2]。 - 缺点:启动稍慢(需要运行时加载库);依赖环境(目标系统必须安装相应库)。 - **运行时机制**:操作系统通过动态链接器(如 Linux 的 `ld-linux.so`)在程序启动时加载库,并解析符号地址。 **核心区别总结**: | 特性 | 静态编译静态链接) | 动态编译(动态链接) | |------------------|------------------------------------|------------------------------------| | **链接阶段** | 库代码嵌入可执行文件 | 只记录库引用,运行时加载 | | **文件大小** | 较大(包含库代码) | 较小(仅引用信息) | | **内存占用** | 较高(每个程序独占库副本) | 较低(多个程序共享库实例) | | **更新维护** | 需重新编译程序 | 只需更新库文件 | | **启动速度** | 较快(无运行时加载开销) | 稍慢(需加载库) | | **依赖要求** | 无外部依赖 | 需目标系统安装库 | | **示例文件** | `.a`(Linux静态库),`.lib`(Windows) | `.so`(Linux共享库),`.dll`(Windows) | **原理深入**: - 静态链接在编译时完成所有地址绑定,生成自包含的二进制文件。这类似于解方程时直接代入值:如果 $a$ $b$ 是已知常量,则 $(a+b)^2$ 在编译时计算结果。 - 动态链接在运行时解析地址,使用位置无关代码(PIC)。这增加了灵活性,但引入了间接寻址开销。例如,函数调用从编译时固定地址变为运行时查表(符号表查找)。 在实际开发中,编译器如GCC通过参数控制链接方式: - 静态链接:`g++ main.cpp -static -o program`(使用 `-static` 标志)。 - 动态链接:`g++ main.cpp -o program`(默认动态链接,库路径通过 `-L` 指定)。 #### 3. **工具管理** 大型项目常用工具管理编译: - **Makefile**:定义编译规则,自动化多文件编译(例如,指定静态或动态链接库)[^3]。 - **CMake**:跨平台的构建系统,生成Makefile或项目文件,简化链接配置。 通过理解这些过程,你可以优化程序性能部署效率。如果需要更深入的代码示例或平台特定细节(如Windows vs Linux),请进一步说明。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值