如果是直接在VS系列的IDE环境下进行C++的开发,这些内容往往是没有太多的用处,对于很多的“快餐族”来说,因为IDE仅仅需要的是拖拉控件,添加代码,一键编译与调试。当真正需要深入地了解这些步骤的时候,编译的过程以及一些相对古老而又传统的工具就显得很重要了。这次的主题主要是针对gcc。
首先,gcc并不是一个编译器,而是一系列有着相似的方式的编译器的集合,因为我们可以使用这一系列的编译器来处理Java,C,C++,Ada等等的源代码。我们的重点是C++。
gcc对应于C++的编译器一般是g++(“一般”是因为我一直认为之前的gcc是可以直接编译C++源码的,但是最近的实验发现必须使用g++)。我们首先要了解的是,一般的程序开发的步骤:
编辑(edit)--- 预处理(preprocess)--- 编译(compile)--- 连接(link)--- 装载(load)--- 执行(execute)
编辑过程不用赘述。预处理和编译的步骤,现在已经被集成到了编译器中,也就是说,在编译器进行编译的过程中,一些与处理过程,像文本替换,插入其他的文本文件等,同时也进行了操作。在gcc中,这样的操作可以通过gcc -c来完成,最终得到的是目标文件。下面是一个例子,来自于《Advanced linux programming》,一共有三个文件,一个C语言的main.c,一个c++的reciprocal.cpp,另外一个是头文件reciprocal.hpp。程序的主要目的是返回一个整数的倒数。
Listing 1.1 (main.c) C source file—main.c
#include
<
stdio.h
>
#include
"
reciprocal.hpp
"
int
main (
int
argc,
char
**
argv)
{
int i;
i = atoi (argv[1]);
printf ("The reciprocal of %d is %g ", i, reciprocal (i));
return 0;
}
Listing 1.2 (reciprocal.cpp) C++ source file—reciprocal.cpp
#include
<
cassert
>
#include
"
reciprocal.hpp
"
double
reciprocal (
int
i)
{
// i should be non-zero.
assert (i != 0);
return 1.0/i;
}
Listing 1.3 (reciprocal.hpp) Header file—reciprocal.hpp
#ifdef __cplusplus
extern
"
C
"
{
#endif
extern double reciprocal (int i);
#ifdef __cplusplus
}
#endif
下面就是对单个文件的编译过程:
% gcc -c main.c
% g++ -c reciprocal.cpp
gcc有着 极为丰富的编译选项,这些内容超出了本文的叙述范围。详细地可以参看http://gcc.gnu.org的说明文档。这里只简单的说几个:优化目标码选项:-O,用法如下:
% g++ -c -O2 reciprocal.cpp
gcc由若干个优化级别(O后面所加的数字)。一般的,级别2适用于较多的情形。但是任何代码的优化所带来的最大的问题是难于跟踪调试。
接下来的就是连接生成可执行的文件。-o表示output,后面加的是生成的可执行文件的名称。之后加的是所有的编译后生成的目标文件。
% g++ -o reciprocal main.o reciprocal.o
接下来,执行文件:
% ./reciprocal 7
The reciprocal of 7 is 0.142857
如上所示,g++仅仅是个编译器。对于单个的源程序文件,简单的编译就已经能够满足需要了。但是对于大型的程序,我们往往面对的事很多的模块,分别存储在不同的源代码文件中。如果我们知道程序之间的依赖性,在我们完成代码之后,直接编译就可以了。但是我们程序员的生活如果是这么的简单,那就天下太平了。我们往往需要做很多的工作在调试、修改和重新的编译上。如果我们还是一次一次的靠输入命令将所有的文件重新的连接和编译,嗯,你觉得会有多慢(或者有多快)?在VS一类的IDE中,这种问题是没有的,因为底层的编译器和项目管理器将这一起接手过来,我们只需要点击按钮就可以了。而现在的原理性问题就在于,我们怎么能够不依赖于IDE环境,来完成我们的这些繁琐复杂的工作。下面给出一个例子,来说明如何使用make来自动化的编译连接所有的源代码。
reciprocal: main.o reciprocal.o
g
++
$(CFLAGS)
-
o reciprocal main.o reciprocal.o
main.o: main.c reciprocal.hpp
gcc $(CFLAGS)
-
c main.c
reciprocal.o: reciprocal.cpp reciprocal.hpp
g
++
$(CFLAGS)
-
c reciprocal.cpp
clean:
rm
-
f
*
.o reciprocal
从上面我们可以看到makefile的格式为:
文件1: 文件2 文件3 ...
规则(编译指令)
这里的生成规则使用了makefile中默认的隐含规则,即如果冒号之前的文件日期要早于冒号后的任何一个文件的日期,相应的规则就会被执行。这只是一个最为简单的例子,更多的功能可以被包含到makefile中,比如说函数,从而带来更多的功能上的便利。更为详细的解说可以参考http://www.linuxsir.org/bbs/showthread.php?t=40431 这篇文章中不仅有关于makefile的详细描述,也给出了一些关于如何划分模块的极好的建议,强烈推荐阅读!
下一次可能会涉及基本的C++语法,或者是标准模板库STL的一些东西,这就要看接下来的进度了。
首先,gcc并不是一个编译器,而是一系列有着相似的方式的编译器的集合,因为我们可以使用这一系列的编译器来处理Java,C,C++,Ada等等的源代码。我们的重点是C++。
gcc对应于C++的编译器一般是g++(“一般”是因为我一直认为之前的gcc是可以直接编译C++源码的,但是最近的实验发现必须使用g++)。我们首先要了解的是,一般的程序开发的步骤:
编辑(edit)--- 预处理(preprocess)--- 编译(compile)--- 连接(link)--- 装载(load)--- 执行(execute)
编辑过程不用赘述。预处理和编译的步骤,现在已经被集成到了编译器中,也就是说,在编译器进行编译的过程中,一些与处理过程,像文本替换,插入其他的文本文件等,同时也进行了操作。在gcc中,这样的操作可以通过gcc -c来完成,最终得到的是目标文件。下面是一个例子,来自于《Advanced linux programming》,一共有三个文件,一个C语言的main.c,一个c++的reciprocal.cpp,另外一个是头文件reciprocal.hpp。程序的主要目的是返回一个整数的倒数。
Listing 1.1 (main.c) C source file—main.c
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
Listing 1.2 (reciprocal.cpp) C++ source file—reciprocal.cpp
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
Listing 1.3 (reciprocal.hpp) Header file—reciprocal.hpp
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
下面就是对单个文件的编译过程:
% gcc -c main.c
% g++ -c reciprocal.cpp
gcc有着 极为丰富的编译选项,这些内容超出了本文的叙述范围。详细地可以参看http://gcc.gnu.org的说明文档。这里只简单的说几个:优化目标码选项:-O,用法如下:
% g++ -c -O2 reciprocal.cpp
gcc由若干个优化级别(O后面所加的数字)。一般的,级别2适用于较多的情形。但是任何代码的优化所带来的最大的问题是难于跟踪调试。
接下来的就是连接生成可执行的文件。-o表示output,后面加的是生成的可执行文件的名称。之后加的是所有的编译后生成的目标文件。
% g++ -o reciprocal main.o reciprocal.o
接下来,执行文件:
% ./reciprocal 7
The reciprocal of 7 is 0.142857
如上所示,g++仅仅是个编译器。对于单个的源程序文件,简单的编译就已经能够满足需要了。但是对于大型的程序,我们往往面对的事很多的模块,分别存储在不同的源代码文件中。如果我们知道程序之间的依赖性,在我们完成代码之后,直接编译就可以了。但是我们程序员的生活如果是这么的简单,那就天下太平了。我们往往需要做很多的工作在调试、修改和重新的编译上。如果我们还是一次一次的靠输入命令将所有的文件重新的连接和编译,嗯,你觉得会有多慢(或者有多快)?在VS一类的IDE中,这种问题是没有的,因为底层的编译器和项目管理器将这一起接手过来,我们只需要点击按钮就可以了。而现在的原理性问题就在于,我们怎么能够不依赖于IDE环境,来完成我们的这些繁琐复杂的工作。下面给出一个例子,来说明如何使用make来自动化的编译连接所有的源代码。
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
从上面我们可以看到makefile的格式为:
文件1: 文件2 文件3 ...
规则(编译指令)
这里的生成规则使用了makefile中默认的隐含规则,即如果冒号之前的文件日期要早于冒号后的任何一个文件的日期,相应的规则就会被执行。这只是一个最为简单的例子,更多的功能可以被包含到makefile中,比如说函数,从而带来更多的功能上的便利。更为详细的解说可以参考http://www.linuxsir.org/bbs/showthread.php?t=40431 这篇文章中不仅有关于makefile的详细描述,也给出了一些关于如何划分模块的极好的建议,强烈推荐阅读!
下一次可能会涉及基本的C++语法,或者是标准模板库STL的一些东西,这就要看接下来的进度了。