猥琐男学C++ 第一课

来源:转载

1.1  编程语言的翻译过程

计算机只认识机器码,所以任何的语言都必须被翻译成机器指令来供计算机读取和执行,通常来讲的话,有2中种翻译器,一种是我们通常所说的编译器,另一种是解释器。

1.2 解释器

那么啥是解释器呢?就是一行一行的代码做解释,一次翻译执行一行,完了后丢掉这一行,任何重复的代码必须重新被解释,所以执行速度上面会慢一点。

现在的解释器当然不是以这种方式执行的,一般比如c#,都是先转换成中间语言,代码被优化一遍,然后由更快的解释器来执行。

解释器的优点就是,写代码和执行代码是立刻完成的,出现错误它可以快速指出,这种方式的语言一般用于快速开发(无需可执行程序)

其实C#也不是要求可执行程序的,因为在托管环境下运行,算不上是真正的可执行的程序

当然项目一旦项目大到一定的程度,而解释器又必须驻留在内存工作,这样的话,不论解释器多么的牛逼,其速度也会变的像蜗牛一样,而且很多的解释器需要输入整段的源代码来做解释工作,造成内存的限制。

现代的解释器,针对这些已经做了很大的优化,但是仍然会或多或少的执行性能不如编译出的可执行程序

1.3 编译器

基于解释器的这些缺点,编译器就产生了。编译器是直接把源代码转换成汇编或者机器指令,这中间比较复杂,分了很多个步骤,所以源代码转换成机器指令是一个比较长的过程。

其优点:

 1. 因为编译器的设计精巧,最终生成的汇编指令都短小精悍,运行速度快。
 2. 分段编译可以分别编译各程序,最后使用连接器把各段程序连接成一个整体程序。
 3. 容易创建大项目,因为可以分段编译的原因,我们可以把各段代码编译运行成功后,加入到库中供其他人使用

1.4 编译过程

 1. 预处理源代码,主要用来处理代码中的预处理指令,当然并不是每种语言都有预处理指令的,所谓预处理指令就是类是#includ #defined 这种东西,可以改变编译器的行为,所以需要在编译还没有正式开始之前就处理。
 2. 编译第一遍:语法分析,将源代码中的最小单元分解成一个数状的数据结构,比如A+B分解成"A" ,"+","B"3个叶子节点
 3. 全局优化器生成更短更小的代码(可选)
 4. 第二遍编译:由代码生成器编译第一遍产生的数,把每个节点转化成汇编的对应的指令,如果生成的是汇编,最后还要对汇编语言进行一次翻译,生成机器代码。
 
总之经过这么多个过程,生成一个 .o 或者 .obj 扩展名的文件,通常也叫Goal

注意与面向对象中的对象(Object)的区别。别弄混

连接器:像它名字一样,它的作用就是用来连接东西的,至于连接神马东西,那当然是编译后的 目标文件了。连接完成的结果就是一个可以在操作系统中装载和运行的程序。

连接器是根据程序中的变量函数引用,来判断需要那些目标模块被连接的,当然最后还要链接一个启动模块来启动程序,要不然程序也是动不了的。

一个库包含多个多个目标模块,其模块是通过库管理器来创建和维护,连接器通常都是通过搜索库文件,来处理所有引用

1.5 关于静态类型检查

就是指编译器的第一遍的时候做类型检查,某些面向对象的语言可以做动态类型检查,比如java C#之类的。很显然动态类型检查会消耗额外的资源去在程序运行是检查类型,所以效率方面当然没有静态类型检查来的好。C++语言本来就是追求效率的嘛。

当然在C++中同样可以实现动态的类型检查。需要写一些额外的代码。以后再说

1.6 分段编译

在C++中可以将一个大的程序划分为一个个小的程序,最小的执行单元当然是函数啰,我们可以将这些最小的执行单元放在单独的不同的文件中来分段编译,如果需要在一个文件中访问另一个文件中的变量或者函数,那你就得告诉编译器,外部函数和数据的模样,然后它才能去找到嘛,寻人启示都得提供照片呢!这个过程在C++里面叫声明。

声明与定义:声明就是向编译器介绍名字(标识符),它只是告诉编译器,这个东西是肯定存在的,你就去找吧。而定义不同,说的是在这里定义,然后还得分配空间,这个是实,那声明是虚的。

定义只能一次,声明随意,有时候定义可以是声明,比如int x;在这个语句之前如果没有出现过x的话,那么编译器把他看成是声明同时也是定义,而且立刻分配存储空间

函数的声明语法:

就像C#中的接口一样
Int func(int,int);
使用的话通过这种方式a = func(4,5);
当然您可以通过给形参命名的方式以方便记忆,是一样的效果。

形参:函数定义或者声明中括号中的参数,就是这东西(int,int)  ,(int length,int weight),实参:a = func(4,5);中的4,5就是实参,形参是虚的实参是实在的

Int func(int length,int weight);

对于这样的函数 int func(); 在C语言中表示可以有任意多的参数,在C++中表示不带任何参数,注意这点

函数定义的语法:
Int func(int length,int weight){}
和声明的区别就是加了2括号。括号中用来写代码的。注意函数定义中的形参必须是有名称的。

变量声明的语法:函数的声明和定义对于编译器来说是很容易区分的,一个有括号,一个没有嘛,但是变量的定义不同,比如  Int x;  这个到底是定义还是声明呢,编译器不好区分,所以C++专门用了一个extern来做标识。
extern int x;
这个表示声明,同样extern可以用在函数上,但其实用不用都无所谓。反正效果都是一样的。

extern int I //这个是声明
extern float f(float);//这个是声明
float b;//同时声明和定义
float f(float a){return a+1;}//定义
int i//定义,因为上面已经声明过了,所以这里就是定义
int h(int x){ return x+1;}//声明同时定义,上面没有出现,定义同时也是声明。
int main(){
b=1.0;
……….
}

1.7 头文件包含

有个问题,就是我使用人家的库的时候,那库的变量函数声明肯定很多,那我不可能一一在我的程序中声明这些变量和函数。所以就需要库的编写者提供一个文件,用来做所有变量和函数的声明,这个文件我们叫头文件,通常扩展名为".h"。

我们要做的是用如下的方式将头文件包含到我们的代码中:
#include "local.h" (先在当前目录下找在通过根据系统定义的查找方式查找文件)
#include <local.h> (根据系统定义的查找方式查找文件)
这样就相当于包含了所有库中函数和变量的声明。简单吧。

标准的C++包含文件格式:因为一致性的关系,C++中通过不带后缀的方式来包含头文件。
#include <local>
使用.h后缀的方式包含的是c语言的库了,当然我们也可以用一下更标准的格式来包含C语言的库,就是在前面加了一个“c”
#include <local.h> == #include <clocal>

1.8 链接:

编译的最后一步,链接器需要知道要链接的目标模块(.o  .obj),以及要连接的库的名称,就可以开始干活了。生成一个可被装载和执行的程序。

可能某些链接器只做一次性链接,这样链接的目标文件和库的顺序就非常关键,不过现代的编译器基本都很智能,这个只要知道就可以了

1.9 使用库文件

了解了这些东西后,我们来看看如何使用库文件
 1. 包含库的头文件
 2. 使用库的变量和函数
 3. 把库连接到可执行文件
 
如果连接器不能在当前的目标模块中找到需要使用的变量和函数时,他就会去库中查找,当然库有库的索引,查找也只是查找索引所以会很快。如果找到就把库中包含变量或者函数定义的那个目标文件包含到当前的程序中,而不是包含整个库。这样就缩小了程序的大小。

基于这一点,我们可以在我们自己做库的时候,将每个函数都放到单独的文件中,这样会很好

需要注意的是,如果碰到相同的定义,链接器是使用先定义的函数或者变量,而忽略后面的定义。

1.10 默认连接的模块

当一个C/C++创建程序时,连接器会默认链接一些模块,比如启动模块,这个模块建立堆栈,并且初始话变量。

对于标准函数,连接器总会去查找标准库,比如<iostream>这个库,不用专门用#include 来做包含就可以使用。相反如果你想使用附加的库,您必须添加库文件名称到链接器处理列表中。

c语言库被包含到C++标准库中,所以在c++中C语言随便用

先说到这里。


分享给朋友:
您可能感兴趣的文章:
随机阅读: