GCC 编译详

来源:互联网 发布:linux终端进度条 编辑:程序博客网 时间:2024/06/05 04:02

GCC 编译详解
GNU CC(简称为Gcc)是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Object C等语言编写的程序。Gcc不仅功能强大,而且可以编译如C、C++、Object C、Java、Fortran、Pascal、Modula-3和Ada等多种语言,而且Gcc又是一个交叉平台编译器,它能够在当前CPU平台上为多种不同体系结构的硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译。本章中的示例,除非特别注明,否则均采用Gcc版本为4.0.0。

GCC入门基础
表3.6 Gcc所支持后缀名解释
后 缀 名 所对应的语言 后 缀 名 所对应的语言
.c C原始程序 .s/.S 汇编语言原始程序
.C/.cc/.cxx C++原始程序 .h 预处理文件(头文件)
.m Objective-C原始程序 .o 目标文件
.i 已经过预处理的C原始程序 .a/.so 编译后的库文件
.ii 已经过预处理的C++原始程序

-D name
Predefine name as a macro, with definition 1.
-D name=definition
Predefine name as a macro, with definition definition. There are no restrictions on the contents of definition, but if you are invoking the preprocessor from a shell or shell-like program you may need to use the shell’s quoting syntax to protect characters such as spaces that have a meaning in the shell syntax.

If you wish to define a function-like macro on the command line, write its argument list with surrounding parentheses before the equals sign (if any). Parentheses are meaningful to most shells, so you will need to quote the option. With sh and csh, ‘-D’name(args…)=definition” works.

‘-D’ and ‘-U’ options are processed in the order they are given on the command line. All ‘-imacros file’ and ‘-include file’ options are processed after all ‘-D’ and ‘-U’ options.
-U name
Cancel any previous definition of name, either built in or provided with a ‘-D’ option.
-undef
Do not predefine any system-specific macros. The common predefined macros remain defined.
-include file
Process file as if #include “file” appeared as the first line of the primary source file. However, the first directory searched for file is the preprocessor’s working directory instead of the directory containing the main source file. If not found there, it is searched for in the remainder of the #include “…” search chain as normal.

If multiple ‘-include’ options are given, the files are included in the order they appear on the command line.
-imacros file
Exactly like ‘-include’, except that any output produced by scanning file is thrown away. Macros it defines remain defined. This allows you to acquire all the macros from a header without also processing its declarations.

All files specified by ‘-imacros’ are processed before all files specified by ‘-include’.

如本章开头提到的,Gcc的编译流程分为了四个步骤,分别为:
• 预处理(Pre-Processing)
• 编译(Compiling)
• 汇编(Assembling)
• 链接(Linking)
下面就具体来查看一下Gcc是如何完成四个步骤的。
首先,有以下hello.c源代码
include

2 “hello.c” 2

int main()
{
printf(“Hello! This is our embedded world!n”);
return 0;
}

由此可见,Gcc确实进行了预处理,它把”stdio.h”的内容插入到hello.i文件中。
(2)编译阶段
接下来进行的是编译阶段,在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译成汇编语言。用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。

[root@localhost Gcc]# Gcc –S hello.i –o hello.s

以下列出了hello.s的内容,可见Gcc已经将其转化为汇编了

(3)汇编阶段
汇编阶段是把编译阶段生成的”.s”文件转成目标文件,读者在此可使用选项”-c”就可看到汇编代码已转化为”.o”的二进制目标代码了。如下所示:

[root@localhost Gcc]# Gcc –c hello.s –o hello.o

(4)链接阶段
在成功编译之后,就进入了链接阶段。在这里涉及到一个重要的概念:函数库。
读者可以重新查看这个小程序,在这个程序中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?最后的答案是:系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,Gcc会到系统默认的搜索路径”/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数”printf”了,而这也就是链接的作用。
函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为”.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为”.so”,如前面所述的libc.so.6就是动态库。Gcc在编译时默认使用动态库。
完成了链接之后,Gcc就可以生成可执行文件,如下所示。

[root@localhost Gcc]# Gcc hello.o –o hello

运行该可执行文件,出现正确的结果如下。

[root@localhost Gcc]# ./hello
Hello! This is our embedded world!
Gcc编译选项分析
Gcc有超过100个的可用选项,主要包括总体选项、告警和出错选项、优化选项和体系结构相关选项。以下对每一类中最常用的选项进行讲解。
(1)总体选项
Gcc的总结选项如表3.7所示,很多在前面的示例中已经有所涉及。
表3.7 Gcc总体选项列表
后缀名 所对应的语言
-c 只是编译不链接,生成目标文件“.o”
-S 只是编译不汇编,生成汇编代码
-E 只进行预编译,不做其他处理
-g 在可执行程序中包含标准调试信息
-o file 把输出文件输出到file里
-v 打印出编译器内部编译各过程的命令行信息和编译器的版本
-I dir 在头文件的搜索路径列表中添加dir目录
-L dir 在库文件的搜索路径列表中添加dir目录
-static 链接静态库
-llibrary 连接名为library的库文件
-### Like ‘-v’ except the commands are not executed and all command arguments are quoted. This is useful for shell scripts to capture the driver-generated command lines.

对于“-c”、“-E”、“-o”、“-S”选项在前一小节中已经讲解了其使用方法,在此主要讲解另外两个非常常用的库依赖选项“-I dir”和“-L dir”。
• “-I dir”
正如上表中所述,“-I dir”选项可以在头文件的搜索路径列表中添加dir目录。由于Linux中头文件都默认放到了“/usr/include/”目录下,因此,当用户希望添加放置在其他位置的头文件时,就可以通过“-I dir”选项来指定,这样,Gcc就会到相应的位置查找对应的目录。
比如在“/root/workplace/Gcc”下有两个文件:

include

include

include

include

The simplest example

hello.o: hello.c hello.h
gcc –c hello.c –o hello.o

接着就可以使用make了。使用make的格式为:make target,这样make就会自动读入Makefile(也可以是首字母小写makefile)并执行对应target的command语句,并会找到相应的依赖文件。如下所示:

[root@localhost makefile]# make hello.o
gcc –c hello.c –o hello.o
[root@localhost makefile]# ls
hello.c hello.h hello.o Makefile

可以看到,Makefile执行了“hello.o”对应的命令语句,并生成了“hello.o”目标体。

注意  在Makefile中的每一个command前必须有“Tab”符,否则在运行make命令时会出错。

Makefile变量
上面示例的Makefile在实际中是几乎不存在的,因为它过于简单,仅包含两个文件和一个命令,在这种情况下完全不必要编写Makefile而只需在Shell中直接输入即可,在实际中使用的Makefile往往是包含很多的文件和命令的,这也是Makefile产生的原因。下面就可给出稍微复杂一些的Makefile进行讲解:

sunq:kang.o yul.o
Gcc kang.o bar.o -o myprog
kang.o : kang.c kang.h head.h
Gcc –Wall –O -g –c kang.c -o kang.o
yul.o : bar.c head.h
Gcc - Wall –O -g –c yul.c -o yul.o

在这个Makefile中有三个目标体(target),分别为sunq、kang.o和yul.o,其中第一个目标体的依赖文件就是后两个目标体。如果用户使用命令“make sunq”,则make管理器就是找到sunq目标体开始执行。
这时,make会自动检查相关文件的时间戳。首先,在检查“kang.o”、“yul.o”和“sunq”三个文件的时间戳之前,它会向下查找那些把“kang.o”或“yul.o”做为目标文件的时间戳。比如,“kang.o”的依赖文件为:“kang.c”、“kang.h”、“head.h”。如果这些文件中任何一个的时间戳比“kang.o”新,则命令“gcc –Wall –O -g –c kang.c -o kang.o”将会执行,从而更新文件“kang.o”。在更新完“kang.o”或“yul.o”之后,make会检查最初的“kang.o”、“yul.o”和“sunq”三个文件,只要文件“kang.o”或“yul.o”中的任比文件时间戳比“sunq”新,则第二行命令就会被执行。这样,make就完成了自动检查时间戳的工作,开始执行编译工作。这也就是Make工作的基本流程。
接下来,为了进一步简化编辑和维护Makefile,make允许在Makefile中创建和使用变量。变量是在Makefile中定义的名字,用来代替一个文本字符串,该文本字符串称为该变量的值。在具体要求下,这些值可以代替目标体、依赖文件、命令以及makefile文件中其它部分。在Makefile中的变量定义有两种方式:一种是递归展开方式,另一种是简单方式。
递归展开方式定义的变量是在引用在该变量时进行替换的,即如果该变量包含了对其他变量的应用,则在引用该变量时一次性将内嵌的变量全部展开,虽然这种类型的变量能够很好地完成用户的指令,但是它也有严重的缺点,如不能在变量后追加内容(因为语句:CFLAGS = [Math Processing Error](VAR)

注意  变量名是不包括“:”、“#”、“=”结尾空格的任何字符串。同时,变量名中包含字母、数字以及下划线以外的情况应尽量避免,因为它们可能在将来被赋予特别的含义。

变量名是大小写敏感的,例如变量名“foo”、“FOO”、和“Foo”代表不同的变量。
推荐在makefile内部使用小写字母作为变量名,预留大写字母作为控制隐含规则参数或用户重载命令选项参数的变量名。

下面给出了上例中用变量替换修改后的Makefile,这里用OBJS代替kang.o和yul.o,用CC代替Gcc,用CFLAGS代替“-Wall -O –g”。这样在以后修改时,就可以只修改变量定义,而不需要修改下面的定义实体,从而大大简化了Makefile维护的工作量。
经变量替换后的Makefile如下所示:

OBJS = kang.o yul.o
CC = Gcc
CFLAGS = -Wall -O -g
sunq : [Math Processing Error](CC) [Math Processing Error](CC) [Math Processing Error](CC) $(CFLAGS) -c yul.c -o yul.o

可以看到,此处变量是以递归展开方式定义的。
Makefile中的变量分为用户自定义变量、预定义变量、自动变量及环境变量。如上例中的OBJS就是用户自定义变量,自定义变量的值由用户自行设定,而预定义变量和自动变量为通常在Makefile都会出现的变量,其中部分有默认值,也就是常见的设定值,当然用户可以对其进行修改。
预定义变量包含了常见编译器、汇编器的名称及其编译选项。下表3.14列出了Makefile中常见预定义变量及其部分默认值。
表3.14 Makefile中常见预定义变量
命 令 格 式 含义
AR 库文件维护程序的名称,默认值为ar
AS 汇编程序的名称,默认值为as
CC C编译器的名称,默认值为cc
CPP C预编译器的名称,默认值为$(CC) –E
CXX C++编译器的名称,默认值为g++
FC FORTRAN编译器的名称,默认值为f77
RM 文件删除程序的名称,默认值为rm –f
ARFLAGS 库文件维护程序的选项,无默认值
ASFLAGS 汇编程序的选项,无默认值
CFLAGS C编译器的选项,无默认值
CPPFLAGS C预编译的选项,无默认值
CXXFLAGS C++编译器的选项,无默认值
FFLAGS FORTRAN编译器的选项,无默认值

可以看出,上例中的CC和CFLAGS是预定义变量,其中由于CC没有采用默认值,因此,需要把“CC=Gcc”明确列出来。
由于常见的Gcc编译语句中通常包含了目标文件和依赖文件,而这些文件在Makefile文件中目标体的一行已经有所体现,因此,为了进一步简化Makefile的编写,就引入了自动变量。自动变量通常可以代表编译语句中出现目标文件和依赖文件等,并且具有本地含义(即下一语句中出现的相同变量代表的是下一语句的目标文件和依赖文件)。下表3.15列出了Makefile中常见自动变量。
表3.15Makefile中常见自动变量
命令格式 含义
[Math Processing Error]+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件
[Math Processing Error]? 所有时间戳比目标文件晚的依赖文件,并以空格分开
命令格式 含义
[Math Processing Error]^ 所有不重复的依赖文件,以空格分开
$% 如果目标是归档成员,则该变量表示目标的归档成员名称

自动变量的书写比较难记,但是在熟练了之后会非常的方便,请读者结合下例中的自动变量改写的Makefile进行记忆。

OBJS = kang.o yul.o
CC = Gcc
CFLAGS = -Wall -O -g
sunq : [Math Processing Error](CC) [Math Processing Error]@
kang.o : kang.c kang.h
[Math Processing Error](CFLAGS) -c [Math Processing Error]@
yul.o : yul.c yul.h
[Math Processing Error](CFLAGS) -c [Math Processing Error]@

另外,在Makefile中还可以使用环境变量。使用环境变量的方法相对比较简单,make在启动时会自动读取系统当前已经定义了的环境变量,并且会创建与之具有相同名称和数值的变量。但是,如果用户在Makefile中定义了相同名称的变量,那么用户自定义变量将会覆盖同名的环境变量。
Makefile规则
Makefile的规则是Make进行处理的依据,它包括了目标体、依赖文件及其之间的命令语句。一般的,Makefile中的一条语句就是一个规则。在上面的例子中,都显示地指出了Makefile中的规则关系,如“[Math Processing Error](CFLAGS) -c [Math Processing Error]@”,但为了简化Makefile的编写,make还定义了隐式规则和模式规则,下面就分别对其进行讲解。
1.隐式规则
隐含规则能够告诉make怎样使用传统的技术完成任务,这样,当用户使用它们时就不必详细指定编译的具体细节,而只需把目标文件列出即可。Make会自动搜索隐式规则目录来确定如何生成目标文件。如上例就可以写成:

OBJS = kang.o yul.o
CC = Gcc
CFLAGS = -Wall -O -g
sunq : [Math Processing Error](CC) [Math Processing Error]@

为什么可以省略后两句呢?因为Make的隐式规则指出:所有“.o”文件都可自动由“.c”文件使用命令“[Math Processing Error](CPPFLAGS) [Math Processing Error](CC) [Math Processing Error](CC) $(CFLAGS) -c yul.c -o yul.o”生成。

注意  在隐式规则只能查找到相同文件名的不同后缀名文件,如”kang.o”文件必须由”kang.c”文件生成。

下表3.16给出了常见的隐式规则目录:
表3.16 Makefile中常见隐式规则目录
对应语言后缀名 规则
C编译:.c变为.o [Math Processing Error](CPPFLAGS) [Math Processing Error](CXX) -c [Math Processing Error](CXXFLAGS)
Pascal编译:.p变为.o [Math Processing Error](PFLAGS)
Fortran编译:.r变为-o [Math Processing Error](FFLAGS)
2.模式规则
模式规则是用来定义相同处理规则的多个文件的。它不同于隐式规则,隐式规则仅仅能够用make默认的变量来进行操作,而模式规则还能引入用户自定义变量,为多个文件建立相同的规则,从而简化Makefile的编写。
模式规则的格式类似于普通规则,这个规则中的相关文件前必须用“%”标明。使用模式规则修改后的Makefile的编写如下:

OBJS = kang.o yul.o
CC = Gcc
CFLAGS = -Wall -O -g
sunq : [Math Processing Error](CC) [Math Processing Error]@
%.o : %.c
[Math Processing Error](CFLAGS) -c [Math Processing Error]@
Make使用
使用make管理器非常简单,只需在make命令的后面键入目标名即可建立指定的目标,如果直接运行make,则建立Makefile中的第一个目标。
此外make还有丰富的命令行选项,可以完成各种不同的功能。下表3.17列出了常用的make命令行选项。
表3.17 make的命令行选项
命令格式 含 义
-C dir 读入指定目录下的Makefile
-f file 读入当前目录下的file文件作为Makefile
命令格式 含 义
-i 忽略所有的命令执行错误
-I dir 指定被包含的Makefile所在目录
-n 只打印要执行的命令,但不执行这些命令
-p 显示make变量数据库和隐含规则
-s 在执行命令时不显示命令
-w 如果make在执行过程中改变目录,则打印当前目录名

查看GCC默认定义的预编译宏
查看到这些预宏定义呢,可以采用gcc -E -dM -

0 0
原创粉丝点击