编译
编译和链接的区别
在多道程序环境中,要想将源代码变成一个可执行的程序,通常分为3个步骤:编译、链接、载入
-
编译:将预处理生成的文件,经过词法分析、语法分析、语义分析以及优化后编译成若干个目标模块。可以理解为将高级语言翻译为计算机可以理解的二进制代码,即机器语言。
-
链接:由链接程序将编译后形成的一组目标模块以及它们所需要的库函数链接在一起,形成一个完整的载入模型。链接主要解决模块间相互引用问题,分为地址和空间分配、符号解析和重定位几个步骤。在编译阶段生成目标文件时,会暂时搁置那些外部引用,而这些外部引用就是在链接时进行确定的,链接器在链接时,会根据符号名称去相应模块中寻找对应符号。待符号确定后,链接器会重写之前那些未确定的符号的地址,这个过程就是重定位。链接一般分为静态链接、载入时动态链接以及运行时动态链接3种
-
载入:有载入程序将载入模块载入内存
编译和链接是为了将用户程序从硬盘上调入内存并将其转换成可执行程序服务的。用编译器时的compile就是在进行编译,link就是链接,运行程序可以看到
以C/C++语言为例,在源文件编译成中间代码文件,在Windows下为.obj
文件,在UNIX、Linux下就是.o
文件,即Object File,该动作被称为编译。然后再把大量的Object File合成执行文件,这个动作称为链接
编译时,编译器需要的是语法正确,函数与变量的声明正确。而一般来说,每个源文件都应该对应于一个中间目标文件(.o
文件或是.obj
文件)。链接时,主要是链接函数和全局变量,所以可以使用这些中间目标文件(.o
文件或是.obj
文件)来链接应用程序
编译型语言与解释型语言的区别
编译型语言
编译是指在应用源程序执行前,就将程序源代码翻译成目标代码(机器语言),因此其目标程序可以脱离其语言环境独立运行,使用比较方便、效率较高。但应用程序一旦需要修改,就必须先修改源代码,再重新编译生成新的目标文件(*.obj
)才能执行,只有目标文件而没有源代码,修改很不方便。现在大多数的编程语言都是编译型的
编译程序将源程序编译成目标程序后保存在另一个文件中,该目标程序可脱离编译程序直接在计算机上多次运行。大多数软件产品都是以目标程序形式发行给用户的,不仅便于直接运行,同时又使他人难于盗用其中的技术,C、C++、Fortran、Visual Foxpro、Pascal、Delphi、Ada都是编译实现的
解释型语言
在解释型语言的实现中,翻译器并不产生目标机器代码,而是产生易于执行的中间代码。这种中间代码与机器代码是不同的,中间代码的解释是由软件支持的,不能直接使用硬件,软件解释器通常会导致执行效率较低。用解释型语言编写的程序是由另一个可以理解中间代码的解释程序执行的
与编译程序不同的是,解释程序的任务是逐一将源程序的语句解释成可执行的机器指令,不需要将源程序翻译成目标代码后再执行。解释程序的优点是:当语句出现语法错误时,可以立即引起程序员的注意,而程序员在程序开发期间就能进行校正。对于解释型Basic语言,需要一个专门的解释器解释执行Basic程序,每条语句只有在执行时才被翻译。这种解释型语言每执行一次,就翻译一次,因而效率低下。一般地,动态语言都是解释型的,如Tcl、Perl、Ruby、VBScript、JavaScript等
需要注意的是,Java是一类特殊的编程语言,Java程序也需要编译,但是却没有直接编译为机器语言,而是编译为字节码,然后在Java虚拟机上以解释方式执行字节码
如何判断一段程序是由C编译的,还是由C++编译的
如果编译器在编译cpp文件,那么__cplusplus
就会被定义,如果是一个C文件在被编译,那么__STDC__
就会被定义。__STDC__
是预定义宏,当它被定义后,编译器将按照ANSIC标准编译C语言程序
所以,可以采用如下程序判断:
#include <stdio.h>
int main()
{
#ifdef __cplusplus
printf("c++");
#else
printf("c");
#endif
return 0;
}
编写C与C++兼容的代码所需的宏如下:
#ifdef __cplusplus
extern "C" {
#endif
//具体的代码
#ifdef __cplusplus
}
#endif
C++程序中调用被C编译器编译后的函数,为什么要加extern “C”
C++语言是一种面向对象的语言,支持函数重载,而C语言是面向过程的编程语言,不支持函数重载,所以函数被C++编译后在库中的名字与C语言不同。如果声明一个C语言函数float f(int a, char b)
,C++的编译器就会将这个名字换成_f_int_char
之类的东西,以支持函数重载。然而,C语言编译器的库一般不进行该转换,所以它的内部名为_f
,这样链接器将无法解释C++对函数f()
的调用
C++提供了C语言Alternate linkage specification(替代链接说明)符号extern "C"
来解决名字匹配问题,它会告诉编译器使用C的规则,而不是C++的规则。例如:
extern "C" float f(int a, char b);
这就告诉C++编译器,函数f是采用C语言方式链接的,应该到库中找名字_f
,而不是找_f_int_char
。C++编译器开发商已经对C标准库的头文件做了extern "C"
处理,所以可以用#include
直接引用这些头文件
如果有一些替代链接的声明,可以把它们放在花括号里:
extern "C"
{
float f(int a, char b);
...//其他函数
}
或者写成
extern "C"
{
#include "Myheader.h"
...//其他C头文件
}
有选择地编译一部分代码
-
在源码中使用条件编译语句,然后在程序文件中定义宏的形式来选择需要的编译代码
-
在源码中使用条件编译语句,然后在编译命令的命令中加入宏定义命令来实现选择编译