The History of GCC
--------------------------------------------------------------------------------
1984年,Richard Stallman发起了自由软件运动,GNU (Gnu's Not Unix)项目应运而生,3年后,最初版
的GCC横空出世,成为第一款可移植、可优化、支持ANSI C的开源C编译器。
GCC最初的全名是GNU C Compiler,之后,随着GCC支持的语言越来越多,它的名称变成了GNU Compiler
Collection。
这里介绍的gcc是GCC的前端,C编译器.
警告信息
--------------------------------------------------------------------------------
-Wall : 显示所有常用的编译警告信息。
-W : 显示更多的常用编译警告,如:变量未使用、一些逻辑错误。
-Wconversion : 警告隐式类型转换。
-Wshadow : 警告影子变量(在代码块中再次声明已声明的变量)
-Wcast-qual :警告指针修改了变量的修饰符。如:指针修改const变量。
-Wwrite-strings : 警告修改const字符串。
-Wtraditional : 警告ANSI编译器与传统C编译器有不同的解释。
-Werror : 即使只有警告信息,也不编译。(gcc默认:若只有警告信息,则进行编译,若有错误信
息,则不编译)
C语言标准
--------------------------------------------------------------------------------
你可以在gcc的命令行中通过指定选项来选择相应的C语言标准: 从传统c到最新的GNU扩展C. 默认情况下,
gcc使用最新的GNU C扩展.
-ansi : 关闭GNU扩展中与ANSI C相抵触的部分。
-pedantic : 关闭所有的GNU扩展。
-std=c89 : 遵循C89标准
-std=c99 : 遵循C99标准
-std=traditional : 使用原始C
注意:后4个选项可以与-ansi结合使用,也可以单独使用。
可在gcc中使用大量GNU C扩展.
生成特定格式的文件
--------------------------------------------------------------------------------
以hello.c为例子,可以设置选项生成hello.i, hello.s, hello.o以及最终的hello文件:
hello.c : 最初的源代码文件;
hello.i : 经过编译预处理的源代码;
hello.s : 汇编处理后的汇编代码;
hello.o : 编译后的目标文件,即含有最终编译出的机器码,但它里面所引用的其他文件中函数的内
存位置尚未定义。
hello / a.out : 最终的可执行文件
(还有.a(静态库文件), .so(动态库文件), .s(汇编源文件)留待以后讨论)
如果你不通过-o指定生成可执行文件名,那么会默认生成a.out. 不指定生成文件名肯能覆盖你上次生成
的a.out.
e.g.
$ gcc hello.c
在不给gcc传递任何参数的情况下, gcc执行默认的操作: 将源文件编译为目标文件--> 将目标文件连接为
可执行文件(名为a.out) --> 删除目标文件.
-c生成.o文件时,默认生成与源代码的主干同名的.o文件。比如对应hello.c生成hello.o. 但也可在生成
目标文件时指定目标文件名(注意同时要给出.o后缀): $ gcc -c -o demo.o demo.c
$ gcc -Wall -c hello.c : 生成hello.o
$ gcc -Wall -c -save-temps hello.c : 生成hello.i, hello.s, hello.o
注意-Wall 选项的使用场合:仅在涉及到编译(即会生成.o文件时,用-Wall)
多文件编译、连接
--------------------------------------------------------------------------------
如果原文件分布于多个文件中:file1.c, file2,c
$ gcc -Wall file1.c file2.c -o name
若对其中一个文件作了修改,则可只重新编译该文件,再连接所有文件:
$ gcc -Wall -c file2.c
$ gcc file1.c file2.o -c name
注意:若编译器在命令行中从左向右顺序读取.o文件,则它们的出现顺序有限制:含有某函数定义的文件
必须出现在含有调用该函数的文件之后。好在GCC无此限制。
编译预处理
--------------------------------------------------------------------------------
以上述的hello.c为例, 要对它进行编译预备处理, 有两种方法: 在gcc中指定-E选项, 或直接调用
cpp.gcc的编译预处理命令程序为cpp,比较新版本的gcc已经将cpp集成了,但仍提供了cpp命令. 可以直
接调用cpp命令, 也可以在gcc中指定-E选项指定它只进行编译预处理.
$ gcc -E hello.c == $ cpp hello.c
上述命令马上将预处理结果显示出来. 不利于观看. 可采用-c将预处理结果保存:
$ gcc -E -c hello.i hello.c == $ cpp -o hello.i hello.c
注意, -c指定名称要给出".i"后缀.
另外, gcc针对编译预处理提供了一些选项:
(1) 除了直接在源代码中用 #define NAME来定义宏外,gcc可在命令行中定义宏:-DNAME(其中NAME为宏
名), 也可对宏赋值: -DNAME=value 注意等号两边不能有空格! 由于宏扩展只是一个替换过程,也可以
将value换成表达式,但要在两边加上双括号: -DNAME="statement"
e.g. $ gcc -Wall -DVALUE="2+2" tmp.c -o tmp
如果不显示地赋值,如上例子,只给出:-DVALUE,gcc将使用默认值:1.
(2) 除了用户定义的宏外, 有一些宏是编译器自动定义的,它们以__开头,运行: $ cpp -dM /dev/null,
可以看到这些宏. 注意, 其中含有不以__开头的非ANSI宏,它们可以通过-ansi选项被禁止。
查看宏扩展
1, 运行 $ gcc -E test.c ,gcc对test.c进行编译预处理,并立马显示结果. (不执行编译) 2, 运行 $
gcc -c -save-temps test.c ,不光产生test.o,还产生test.i, test.s,前者是编译预处理结果, 后者
是汇编结果.
利用Emacs查看编译预处理结果
针对含有编译预处理命令的代码,可以利用emacs方便地查看预处理结果,而不需执行编译,更为方便的
是,可以只选取一段代码,而非整个文件:
1,选择想要查看的代码
2,C-c C-e (M-x c-macro-expand)
这样,就自动在一个名为"Macroexpansion"的buffer中显示pre-processed结果.
生成汇编代码
--------------------------------------------------------------------------------
使用"-S"选项指定gcc生成以".s"为后缀的汇编代码:
$ gcc -S hello.c
$ gcc -S -o hello.s hello.c
生成汇编语言的格式取决于目标平台. 另外, 如果是多个.c文件, 那么针对每一个.c文件生成一个.s文件
.
包含头文件
--------------------------------------------------------------------------------
在程序中包含与连接库对应的头文件是很重要的方面,要使用库,就一定要能正确地引用头文件。一般在
代码中通过#include引入头文件, 如果头文件位于系统默认的包含路径(/usr/includes), 则只需在
#include中给出头文件的名字, 不需指定完整路径. 但若要包含的头文件位于系统默认包含路径之外,
则有其它的工作要做: 可以(在源文件中)同时指定头文件的全路径. 但考虑到可移植性,最好通过-I在调
用gcc的编译命令中指定。
下面看这个求立方的小程序(阴影语句表示刚开始不存在):
#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[])
{
double x = pow (2.0, 3.0);
printf("The cube of 2.0 is %f\n", x);
return 0;
}
使用gcc-2.95来编译它(-lm选项在后面的连接选项中有介绍, 这里只讨论头文件的包含问题):
$ gcc-2.95 -Wall pow.c -lm -o pow_2.95
pow.c: In function `main':
pow.c:5: warning: implicit declaration of function `pow'
程序编译成功,但gcc给出警告: pow函数隐式声明。
$ ./pow_2.95
The cube of 2.0 is 1.000000
明显执行结果是错误的,在源程序中引入头文件(#include <math.h>),消除了错误。
不要忽略Warning信息!它可能预示着,程序虽然编译成功,但运行结果可能有错。故,起码加上"-Wall"
编译选项!并尽量修正Warning警告。
搜索路径
首先要理解 #include<file.h>和#include"file.h"的区别:
#include<file.h>只在默认的系统包含路径搜索头文件
#include"file.h"首先在当前目录搜索头文件, 若头文件不位于当前目录, 则到系统默认的包含路径搜索
头文件.
UNIX类系统默认的系统路径为:
头文件,包含路径: /usr/local/include/ or /usr/include/
库文件,连接路径: /usr/local/lib/ or /usr/lib/
对于标准c库(glibc或其它c库)的头文件, 我们可以直接在源文件中使用#include <file.h>来引入头文件
.