您的位置:首页 > 其它

[译] C track: compiling C programs.

2015-08-22 13:53 344 查看
原文:C track: compiling C programs.

C track: compiling C programs.

尽管有些计算机语言(如 Schema 或者 Basic)通常使用交互式的解释器(当你输入命令后,就可立即执行),但 C 语言不是。C 的源文件总是要通过一个叫做编译器(compiler)的程序编译成二进制代码然后运行。这就是我们接下来要详细说明的几个步骤。

几种不同类型的文件

你需要4种文件进行编译C 程序:

常规的源代码文件(source code)。 这些文件包含了函数定义,并约定以 "
.c
" 作为结尾进行命名。

头文件(Header). 这些文件包含了函数声明(也叫做函数原型)以及各种预处理语句。源文件可以通过头文件访问外部定义的函数。头文件的文件名约定以 "
.h
" 作为结尾.

目标文件(Object). 这些文件由编译器的输出而产生。目标文件包含了二进制形式的函数定义,本身是不可执行文件。目标文件的文件名约定以"
.o
" 结尾,尽管在一些操作系统,如(Windows, MS-DOS),经常以"
.obj
" 结尾。

二进制可执行文件(Binary executables)。这些文件由一个叫做链接器(linker)的程序的输出而产生。链接器链接一些目标文件并产生可以直接执行的二进制文件。二进制可执行文件在 Unix 操作系统上没有后缀名,但在 Windows 上,通常以"
.exe
" 作为后缀名。

还有其他的各种文件,尤其是静态库文件("
.a
" files or ".lib" on Windows)以及共享库文件("
.so
" files or ".dll" on Windows)。但通常,你不需要直接与他们打交道。

预处理

在编译器开始编译源文件之前,源文件由预处理器(preprocessor)进行处理。预处理器是一个真实的单独的程序(通常叫做"
cpp
", for "C preprocessor"),而由编译器在编译前自动调用。预处理器的工作就是讲源文件转换成另外一个源文件(你也可以认为是对源文件的修改或者扩展)。修改后的文件可能作为一个真实的文件存在文件系统中,也可能仅仅是在发送给编译器之前在内存中作短暂的保留。另外,你不需要特别关注预处理,但是你需要知道预处理是干什么滴。

预处理指令以符号("
#
")开始. 在多种预处理指令中,有两种最为重要:

#define
. 主要用于定义常量。如,

#define BIGNUM 1000000

指定在剩下的程序中任何位置处理的字符串 BIGNUM 应该被替换为 1000000。例如,这个语句:

int a = BIGNUM;

变成了

int a = 1000000;

#define
语句用于避免一个常量值在源文件中多处重复出现。这在你随后需要对这常量值进行修改时是相当的重要,并且可以减少bug 的滋生,你只需要对 #define 的定义修改,而不是对常量值在整个源代码中多处的出现位置进行修改。

#include
. 用于访问位于源文件之外的函数定义。例如:

#include<stdio.h>

在源代码编译之前,预处理器将<stdio.h> 的内容替换
#include
语句所在的位置。
#include
总是用于主要包含函数声明和#define 语句的头文件。 这时,我们可以通过
#include
语句而使用一些函数,如
printf
scanf
, 这两个函数的声明就位于文件
stdio.h 中
. 在源文件中,在函数声明或者定义之前,C compilers 是不允许我们使用函数的;
#include
语句就是用于这种情况,从而使我们可以复用之前用C 编写的代码。

还有其他的各种预处理指令,我们将会根据需要进行有所处理。

生成目标文件: 编译器

在 C 预处理器包含了所有的头文件并且展开所有的
#define
#include
语句(也有其他一些在源文件中出现的预处理指令)后,编译器就可以编译程序了。编译器将 C 源文件编译成目标文件(object code),包含二进制版本源代码并以 ".o" 结尾的文件。 然而,目标文件并不能直接运行。为了能够生成可执行文件,你还需要加入被
#include
d 包含的库函数代码(这个通过 #include 包含函数声明是不一样的)。这就是下一节要讲到的链接器 linker 的工作。

通常,编译由以下方式被调用:

% gcc -c foo.c

符号
%
是 unix 提示符. 它告诉编译器对文件
foo.c
运行预处理程序并编译成目标文件
foo.o。
-c
选项意思是由编译器将源文件编译成目标文件而不会调用链接器。如果你的整个程序就一个源文件,你也可以这么做:

% gcc foo.c -o foo

它告诉编译器在文件
foo.c 运行预处理器,编译并链接产生一个可执行文件
foo。
-o
表示二进制可执行文件(程序)将以其随后的单词作为文件名。 如果你不制定
-o 选项,或者仅仅是输入
gcc foo.c,由于某种历史原因,可执行文件将以
a.out
命名。

请注意编译器的名字,我们使用的是
gcc,
代表 "GNU C compiler" 或者 "GNU compiler collection" 。也有其他的编译器;他们中大多数都以
cc("C compiler")命名。在
Linux 操作系统中
cc 是
gcc 的别名。


揉成一团: 链接器

链接器的工作就是将一组目标文件(.o 文件)一起链接到一个二进制可执行文件。这包括从你的源代码文件编译的目标文件,以及预编译的库文件(library files)。 这些文件
.a
或者
.so 作为结尾命名,通常你不需要知道他们,因为他们中大多数可以由链接器(
linker)定位并根据需要自动链接。

像预处理器一样,链接器也是一个叫做 ld 独立的程序。也如预处理器一样,链接器在你使用编译器时自动被调用。链接器通常使用的方式如下:

% gcc foo.o bar.o baz.o -o myprog

这一行是告诉编译器一起将三个目标文件(
foo.o
,
bar.o
, and
baz.o)
链接成一个名为
myprog 的二进制可执行文件
.

这就是你需要知道如何编译你的 C 程序的事情。通常,我们也推荐
-Wall
选项:

% gcc -Wall -c foo.cc

-Wall
选项让编译器对合法但是可以的代码结构发出警告,并且帮助你轻松捕获一些 bugs。如果你想要更多的编译检查项:

% gcc -Wall -Wstrict-prototypes -ansi -pedantic -c foo.cc

-Wstrict-prototypes
选项是让编译器对代码中没有正确原型的函数做出警告。
-ansi
-pedantic
是让编译器对代码中不可移植的结构(e.g. 一些在 gcc 中合法而不满足标准 C compilers 的代码结构;这些结构通常是需要避免的)做出警告。

References

Kernighan and Ritchie, The C Programming Language, 2nd Ed.

The man page for
gcc
. Type:
man gcc
at the unix prompt.

The GNU Info documentation on
gcc
. Warning! This is far more information than most people could possibly absorb in the average millenium.

Info documentation on
gcc
can be accessed through the GNU emacs editor by typing "M-x info" (where "M-x" means to hit the meta-key and "x" simultaneously), or "C-h i" (where "C-h" means to hit the control key and "i" simultaneously), followed by "mgcc<return>". Type "minfo<return>" instead for a quick tour of how to use info. You can also access the info documentation from the unix command line by typing
info gcc
.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: