您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 订阅
  捐助
C++的编译过程及原理
 
作者: qq_43133135
   次浏览      
 2019-11-28 
 
编辑推荐:
文章本次内容是关于c++编译过程的,内容如下: C和C++的程序结构 预编译 编译过程及链接,希望对您有所帮助。
文章来自于csdn,由火龙果Delores编辑推荐。

C和C++程序结构

我们来看一个基本程序,由animal.h ,animal.cpp,human.h,human.cpp ,main.cpp等5个文件组成:

------------------------------
animal.h 头文件 这样写:
------------------------------
#ifndef _animal_H
#define _animal_H
#include "iostream.h"
class animal
{
public:
int move;
void out_put();
}
void animal :: out_put()
{
cout<<"move="<<move<<endl;
}
extern animal Dongwu; //加了extern
关键字声明定义在其他文件中
void show();//函数声明 #endif

------------------------------
animal.cpp 文件 这样写:
------------------------------
#include "animal.h"animal Dongwu;
//定义animal对象 Dongwu
void show()
{
cout<<"show move="<<Dongwu.move<<endl;
}

------------------------------
human.h 头文件 这样写:
------------------------------
#ifndef _human_H
#define _human_H
#include "animal.h" //human类
要从animal类 中继承
#include "iostream.h"
class human: public animal
{
public:
int thought;
}
void showme();//函数声明
#endif

------------------------------
animal.cpp 文件 这样写:
------------------------------
#include "animal.h"
human me; //定义human对象 me
void showme()
{
cout<<"thought="<<human.move<<endl;
}


```c
------------------------------
main .cpp 主函数文件写法:
------------------------------
#include "human.h"
#include <iostream.h>
void main()
{
animal.out_put();
show();
showme();
}

我们发现,但凡是声明一般都放在了头文件中,比如animal类的声明以及show();等函数的声明。 但是为什么这么做呢?我们接下来将会说明。

预编译

我们发现了头文件中有一些带#开头的关键字,如:#define,#ifndef,#endif,等等。

这阶段是预处理阶段,比如说·#define m 5,那么在该阶段会将程序中的m全部替换成5

想必对于#define,大家都熟悉,接下来我们说说条件编译的关键字:

条件编译指令:#ifdef,#ifndef,#else,#elif,#endif等。

我们通常这样使用:

#ifndef xxx
#define xxx
..............
..............
#else
.............
......
#endif

如果这是程序中的条件选择,想必大家就很好理解了:

if ( xxx==Null)
{
xxx=true;
..............
..............
}
else
{ .............
......
}

只不过,程序中的条件选择是在程序运行的时候才被执行的,而条件编译是在预编译时执行的条件选择。

那么条件编译有什么作用呢?

#ifndef Linux
linux平台下运行的函数
#else
#ifndef windows
Windows平台下运行的函数
#endif
#endif

我们可以看到,这样可以兼容不同的平台,想在linux平台下运行,只要在条件编译前添加#define Linux就好了

另外,也可以通过这种方式来选择不接入某些不需要用的模块,提高编译速度

除此之外,还有一个作用,举个例子,头文件中这样写:

#ifndef _human_H
#define _human_H
#include "animal.h" //human类 要从animal类 中继承
#include "iostream.h"
class human: public animal
{
public:
int thought;
}
void showme();//函数声明
#endif

我们可以把每一个文件看做一个函数,用函数来理解过程就容易多了:

void human()
{
static _human_H=false;
if(_human_H==false)
// #ifndef _human_H
{
_human_H =true; // #define _human_H
animal(); // #include "animal.h"
iostream(); // #include "iostream.h"
......待编译内容
......待编译内容
} // #endif
}

假设每一个头文件都是一个函数,每个文件中每#include一个头文件的时候,就相当于调用一次那个函数。 用了条件编译,我们可以保证,每个头文件只调用一次,但是为什么要只调用一次?

假设两个函数这样:

void animal()
{
static _animal_H=false;

human();
}
void human()
{
static _human_H=false;

animal(); // #include "animal.h"
}

你猜会发生什么,是不是会死循环?两个函数相互调用直到天荒地老。并且调用多次编译器会显示重复定义的错误。

如果加了条件选择:

void animal()
{
static _animal_H=false;
if(_animal_H==false)
{
_animal_H=ture;
human();
}
}
void human()
{
static _human_H=false;
if(_human_H==false)
{
_human_H=true;

animal(); // #include "animal.h"
}
}

这样我们能保证每个函数只调用一次,而不会相互一直调用

也正是因为这样,所以我们才能放心大胆的随便 #include:

------------------------------
animal.h 头文件 这样写:
------------------------------
#ifndef _animal_H
#define _animal_H
#include "human.h"
.......
#endif

------------------------------
human.h 头文件 这样写:
------------------------------
#ifndef _human_H
#define _human_H
#include "animal.h" //human类
要从animal类 中继承
.........
#endif

我之所以用函数的运行来举例说明,那是因为每个#include "animal.h"实际上就是用animal.h文件的内容将其替换。#define 的作用是替换单个符号,而#include的作用是将这个#include用其include的头文件进行替换。如果将文件抽象为函数,那么本质上并没有什么太大区别。

接下来说明一下预编译过程,以及为什么#include一般放在文件开头:

我们刚开始学c++的时候,程序都是很小的,所以都写在同一个文件中:

#include "iostream.h"
class animal
{
public:
int move;
void out_put();
}
void animal :: out_put()
{
cout<<"move="<<move<<endl;
}
animal Dongwu;
//定义animal对象 Dongwu
void show();
//函数声明void main()
{
animal.out_put();
show();
}
void show()
{
cout<<"show move=
"<<Dongwu.move<<endl;
}

但是随着文件越来越大,这种方式是不现实的,看上去就特别乱,我们知道,调用一个函数之前,必须先能找到他的声明,所以我们将各种要调用的函数声明打包成头文件,并将其添加到main函数之前,在预编译过程中,#include将会被其文件内容替换,从而实现将函数声明放在main之前,为main函数中调用打下基础,这就是为什么#include为什么被放在cpp文件的开始部分。

另外这里对#include“animal.h”和#include < animal.h >进行说明一下区别

.

<>和“”表示编译器搜索头文件的顺序不同:

<>表示从系统目录下开始搜索,然后再搜索PATH环境变量所列出的目录,不搜索当前目录

""表示先从当前目录搜索,然后是系统目录和PATH环境变量所列出的目录下搜索

.

所以如果我们知道头文件在系统目录或者环境变量目录下时,可以用<>来加快搜索速度。

编译过程及链接

当我们点击编译按钮时,通常会出现如下提示:

Compiling...
animal.cpp
human.cpp
...
Linking...
main.exe - 0 error(s),
0 waring(s)

这段文字输出事实上已经说明了编译的步骤了

编译器先对工程中三个源文件main.cpp,animal.cpp,human.cpp进行单独编译 (Compiling...)

在编译时,由预处理器对预处理指令(#include、#define…)进行处理,在内存中输出翻译单元(就是将include等在源文件上替换了以后产生的临时文件)。

编译器接受临时文件,将其翻译成包含机器语言指令的三个目标文件(main.obj、animal.obj、human.obj) 接下去就是链接过程(Linking...),连接器将目标文件和你用到的相关库文件一起链接形成main.exe。 到此,编译也就结束了。

注意:在编译过程中头文件不参与编译,预编译时进行各种替换以后,头文件就完成了其光荣使命,不再具有任何作用

最后以一张图来结束本次内容:

   
次浏览       
相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程