C++学习笔记(2)——程序的创建、编译与调试

单位新来的同事(也是非科班出生)问我编程用什么软件时,我大多会告诉他们VS2010或者VS2008(原谅我们单位用的IDE还如此老旧)。他们便会找来VS2010或者VS2008的教程,一步步照着做起来。新建工程,写了一个main函数,打印了“helloworld”,点击了build和debug,看到一个控制台程序一闪而过,似乎是打印成功了,有的还会问我为什么一闪而过了。想想我当初也是这样的感觉,我根本不明白上述一系列操作我到底干了些什么,为什么要这么做。本篇笔记将梳理这一过程。

一、如何让程序运行起来

如何让自己编写的程序运行起来,不同的平台可能具体的方法不一样,但是其根本步骤是一样的:

编写源代码——预处理(Prepressing)——编译(Compilation)——汇编(Assembly)——链接(Linking)——执行(Executing)

1.编写源代码

源代码不是必须在某个IDE(集成开发环境,如VS2010)下才能编辑的。在windows下你完全可以新建一个txt文档,在里面输入以下源码:

//My first cpp——hello world         //C++注释
#include <iostream>					//预处理器
using namespace std; 				//名称空间

int main()							//主函数名
{
	cout<<"hello, world!"<<endl;	//输出流
	cin.get();					//等待输入
	return 0;						//返回值
}

以上的源码虽然仅有几行代码,但麻雀虽小五脏俱全,它包含了预处理器,头文件,名称空间,函数,C++类,C++输入输出流等一系列元素,其中的cin.get()会让程序停留下来,不至于出现开始我的同时小白提出的一闪而过的问题。这些元素我们将来一定会深入研究。现在只要将这个文件的后缀改为cpp,他就会被编译器认为是c++源代码,也即可以对它进行编译了。实际上在一些平台上,像C,cc,cxx,cp,c++都会被认为是C++源代码文件。但是cpp应用更为广泛。

有的源码会有一个头文件,通常见到的是.h文件,但其实在C++标准中,.h是C语言的头文件,而C++的头文件是没有后缀的,就像上述代码中的iostream其实就是一个头文件。但是人们为了明确表示头文件和适应C语言,通常将.h文件作为c++的头文件。现在很多主流编译器也会将.hpp或.hxx认为是C++头文件处理。

2.预处理

预处理过程主要处理那些源代码文件以“#”开始的预编译指令。比如“#include”、“#define”和条件预编译指令,如“#if”、“#ifdef”等。预处理时,将所有的“#define”删除,展开所有的宏定义,并且替换掉“#include”。

(1)宏定义指令,如#define a b。对于这种伪指令,预编译所要做的是将程序中的所有a用b替换,还有#undef,则将取消对某个宏的定义,使以后该串的出现不再被替换。

(2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif等。这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。

(3) 头文件包含指令,如#include"FileName"或者#include<FileName>等。两者的区别是:系统提供的头文件包含用尖括号,系统直接去系统目录查找文件;自己写的用双引号,系统从工程目录中查找,如果没有再去系统目录查找文件。在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。采用头文件的目的主要是为了使某些定义可以供多个不同的源程序使用。因为在需要用到这些定义的源程序中,只需加上一条#include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。

预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同,经过预编译后产生完整的源文件,将此文件作为编译程序的输入而被翻译成为机器指令。

3.编译

经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C++关键字等。为了使计算机能执行高级语言源程序,必须先用一种称为“编译器(complier)”的软件(也称编译程序或编译系统)。编译是以源程序文件为单位单别编译的,头文件不参加编译。(在VC6.0里如果编译头文件则会弹出没有可以工具函数,在VS2013中,对于头文件,编译按钮为灰色,不可用状态。)

编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析以及优化后产生相应的汇编代码文件,这个过程是整个程序构建的核心部分,也是最复杂的部分之一。编译时对文件中的全部内容进行检查,如果有语法错误,编译结束后会显示出所有的编译出错信息,开发人员可以根据错误提示修改程序。

现在编译器种类很多,不同编译器区别在于对编译过程做了优化,添加了一些库函数或类库。优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放的有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。

对于新写的一个包括多个文件的工程,一开始采用源文件分别编译,这样容易发现每个源文件的自身错误,限定了错误的范围,如果一开始就采用全部编译,多个源文件可能会产生许多错误,无形中增加了开发难度。如果每个源文件都通过了编译,再将所有文件进行编译。对源文件分别编译对于调试,纠错是一种很好的方法。

4.汇编

汇编实际上指把汇编语言代码翻译成目标机器指令的过程。汇编器的编译过程相对于编译器来讲比较简单,它没有复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译。对于被翻译系统处理的每一个语言源程序,都将最终经过这一处理而得到相应的目标文件。目标程序一般以.obj或.o作为后缀,这具体看操作系统,如Windows是下是.obj目标文件,Linux下是.o目标文件。目标文件中所存放的也就是与源程序等效的目标机器语言代码。

通常,我们也将预编译、编译和汇编统称为编译。

5.将目标文件连接

前面提到过,编译是对源文件分别进行的,每个源文件都产生一个目标文件。但由汇编程序生成的目标文件并不能立即就被执行,因为各个源文件之间可能是有相互联系的,例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题都需要经链接解决,即将源程序产生的多个目标文件链接为一个整体。即通过系统提供的“链接程序(linker)”将一个程序的所有目标程序和系统的库文件以及系统提供的其他信息连接起来,最终形成一个可执行的二进制文件,它的后缀是.exe(windows下),此时产生了完整的执行文件。

链接程序的主要工作就是将有关的目标文件彼此相连接,如源文件产生的目标文件和库文件等,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。根据指定的库函数的不同,链接处理可分为两种:

(1)静态链接:在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

(2)动态链接:此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。

链接将相关关联文件链接起来,所以这个阶段的错误不好调试,发生错误可能在我们自己编写的代码中,也有可能是与别的文件关联产生的,对于因关联产生错误就比较复杂了,有时需要调整编译器或链接器。

6.运行程序

运行阶段就比较简单了,直接执行前面链接过程产生的可执行的二进制文件(windows下的.exe文件)即可得到运行结果。通过对运行结果的分析,检验设计的程序是否满足期望和要求。如果运行结果不正确,应检查程序或算法,重新编辑代码。

二、编译器

C++编译器是源代码到可执行程序的重要纽带。它的作用促使它必须与C++同时发展。

1985年10月,Cfront Release 1.0发布。

1987年12月,GNU C++发布。

1988年1 月,第一个Oregon Software C++发布。

6 月,第一个Zortech C++发布。

1990年5 月,第一个Borland C++发布。

1992年2 月,第一个Dec C++发布。

3 月,第一个Microsoft C++发布。

5 月,第一个IBM C++发布。

现如今,C++的各色编译器比比皆是,而且形成了一些列出色的集成开发环境(IDE)。

1.常见的编译器有:

MSVC是微软Windows平台Visual Studio自带的C/C++编译器。

GCC原名GNU C Compiler,后来逐渐支持更多的语言编译(C++、Fortran、Pascal、Objective-C、Java、Ada、Go等),所以变成了GNU Compiler Collection(GNU编译器套装),是一套由GNU工程开发的支持多种编程语言的编译器。GCC是自由软件发展过程中的著名例子,由自由软件基金会以GPL协议发布,是大多数类Unix(如Linux、BSD、Mac OS X等)的标准编译器,而且适用于Windows(借助其他移植项目实现的,比如MingW、Cygwin等)。GCC支持多种计算机体系芯片,如x86、ARM,并已移植到其他多种硬件平台。

Cygwin是一个Windows下Unix-like模拟环境,具体说就是Unix-like接口(OS API,命令行)重定向层,其目的是不修改软件源码仅重新编译就可以将Unix-like系统上的软件移植到Windows上(这个移植也许还算不上严格意义上的无缝移植)。始于1995年,最初作为Cygnus软件公司工程师Steve Chamberlain的一个项目。Cygwin是让Windows拥有Unix-like环境的软件而不是编译器,GCC是安装在Cygwin上的编译器。

MingW(Minimalist GNU on Windows)是一个Linux/Windows下的可以把软件源码中Unix-like OS API调用通过头文件翻译替换成相应的Windows API调用的编译环境,其目的和Cygwin相同。从而把Linux上的软件在不修改源码的情况下编译为可直接在Win下执行的exe。

注意:与Windows下其它编译器不同的是,MinGW与Linux下广泛使用的GNU近乎完全兼容,这意味着,在Linux下如何编译源代码,在MinGW中也可以以完全相同的方式编译。有些Linux下的开发人员(比如开源阵营)发布的源代码通常只提供Linux下的编译方式,而不提供Windows下的编译方式(这可能与其不熟悉windows操作系统有关),但确实有不少用户需要在在Windows下编译使用此源代码。这在种情况下,如果Windows用户想用VC、BC等编译器编译该源代码,必须重写Makefile(各种编译器所支持的Makefile不尽相同),工作量比较大不说,还很难保证不出错。MinGW的出现,提供了两个平台下的“跨平台编译方案”。MinGW与MSYS相配合,连./configure都有了。与GNU不同的是,MinGW编译生成的是Windows下的可执行文件(.exe)或库文件(.dll,.lib)——不过编译过程中的的中间文件仍然是.o文件,而不是.obj文件(这当然无所谓了,中间文件嘛,编译完成后就没有用了)。

2.常见的集成环境:

code block是最流行的免费开源的集成环境之一,这个IDE包括所有开发者需要的功能,比如:高效的用户界面,编译器和调试器等等。

Eclipse CDT是最权威也是最流行的IDE,提供超多高效的特性:支持项目创建和各种工具链的管理,标准make构建,源代码导航,各种源知识工具,有语法高亮显示功能的代码编辑器,可视化的调试工具,博阿奎内存,寄存器和反汇编查看器。

compiler是个在线集成开发工具,允许用户使用工具来编写代码和创建简单的用户界面。此工具支持超级多的编程语言,比如C,C++,Java,HTML等等。

Code lite是另一个超级棒的免费开源的C和C++的IDE,可以在所有流行的平台上运行(Windows,OS X,Linux 等)。

Dev C++是个全功能的IDE,有以下特性:1) 类查看器 2) 集成调试 3) 支持基于GCC的编译器  4) 项目管理 5) 编译器 6) 编辑和编译资源文件 7) 工具管理器 8) 函数列表。

Ultimate++ 是个杰出的 C++ 开发框架,这个IDE包括C++编程的模块化概念,可以使用GCC,MinGW和Visual C++。

Visual Studio 是微软旗下的大型代码开发环境,它不仅支持C++,还支持诸如java,C#等很多流行的编程语言,并在新版本中加入了跨平台开发环境。而且它很适合新手入门使用。我的该系列C++学习笔记的代码将都在VS2017下开发。

三、windows下的源码编译实战

在VS2017环境下编译调试这里不再赘述。这里总结一下在MinGW上的代码开发编译过程,因为这款C++编译环境支持跨平台,是当前windows下通过命令行的方式编译C++程序最好用的环境之一。。

1.MinGW的下载安装

官网:http://www.mingw.org/

下载安装程序:https://sourceforge.net/projects/mingw/files/Installer/

下载下来的是mingw-get-setup.exe,这是一个安装且而已,也就是说运行这个软件,他会在网上自动下载安装需要的安装包进行安装。如此看MinGW的安装比较简单。但如果你想离线安装它,恐怕就比较困难了,因为每个电脑需要的安装文件都不太一样。

点击“install”,等待就好……

之后continue,选择你要安装的组件(mingw32-gcc-g++必选)

点击Installation->Apply changes->Apply,这个过程比较漫长。。。

添加环境变量:我的电脑->属性->更改设置->高级->环境变量,在用户变量的path中添加一条:C:\Mingw\bin

现在运行 cmd 指令 再输入 “gcc -v”:

2.编译程序

在cmd窗口中用 cd命令打开刚刚创建的cpp文件所在目录,输入

g++ helloworld.cpp  回车

便会自动编译该源码,并在该目录下生成一个a.exe的可执行文件,双击运行该文件,比那会出现打印“hello world”。

重新输入

g++ -o helloworld.exe helloworld.cpp

则会生成一个helloworld.exe文件。

然后同样在该目录下新建一个makefile文件(用文本文件创建),输入:

helloworld.exe:  

       g++ -o helloworld.exe helloworld.cpp

注意所有含有gcc的行前面是一个制表符,而非若干空格。否则可能会保存,无法编译。

在cmd中输入mingw32-make,则可重新编译生成helloworld.exe

在C:\MinGW\bin下将mingw32-make.exe改为make.exe。则可以直接通过运行make实现编译,这看起来更像是Linux下的make了。

这里的makefile是大多程序编译时遵循的法则。诸如VS2017这种大型集成开发环境,你可能点击了bulid按钮,实现了程序的编译,实际上也生成了一系列复杂的makefile,进而根据这些makefile生成可执行文件。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bjtuwayne

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值