准备工作
如果你还没装~译环境或自׃定装没装,不妨先执?/p>
sudo apt-get install build-essential
如果你需要编?Fortran E序Q那么还需要安?gfortran(?g77)
sudo apt-get install gfortran
如果你已l了解一?vim 的知识,而且想用它来~辑源代码,那么我们不妨装个完整?/p>
sudo apt-get install vim-full
如果你不了解vimQ选择gedit、kate或mousepad来编辑源代码好?/p>
注意Q?/strong>本文可能会让你失望,如果你看完后有下列疑问的话:Z么要在终端输命o啊? GCC 是什么东西,怎么在菜单中找不刎ͼ GCC 不能有像 VC 那样的窗口吗Q?#8230;… 那么你真正想要了解的可能?anjutaQkdevelopQgeanyQcode blocksQeclipseQneatbean {?IDE 集成开发环?/a>。即使在q种情况下,׃ GCC 是以?IDE 的后台的~译器,本文仍值得你稍作了解?/p>
~译单的 C E序
C 语言l典的入门例子是 Hello WorldQ下面是一CZ代码Q?/p>
#include <stdio.h>
int
main(void)
{
printf("Hello, world!\n");
return 0;
}
我们假定该代码存为文?#8216;hello.c’。要?gcc ~译该文Ӟ使用下面的命令:
$ gcc -Wall hello.c -o hello
该命令将文g‘hello.c’中的代码~译为机器码q存储在可执行文?‘hello’中。机器码的文件名是通过 -o 选项指定的。该选项通常作ؓ命o行中的最后一个参数。如果被省略Q输出文仉认ؓ ‘a.out’?/p>
注意?/strong>如果当前目录中与可执行文仉名的文g已经存在Q它被复盖?/p>
选项 -Wall 开启编译器几乎所有常用的警告──强烈你始l用该选项。编译器有很多其他的警告选项Q但 -Wall 是最常用的。默认情况下GCC 不会产生M警告信息。当~写 C ?C++ E序时编译器警告非常有助于检程序存在的问题?/p>
本例中,~译器用了 -Wall 选项而没产生M警告Q因为示例程序是完全合法的?/p>
要运行该E序Q输入可执行文g的\径如下:
$ ./hello
Hello, world!
q将可执行文件蝲入内存,q CPU 开始执行其包含的指令?路径 ./ 指代当前目录Q因?./hello 载入q执行当前目录下的可执行文g ‘hello’?/p>
点击此处下蝲本节的操作视?/p>
捕捉错误
如上所qͼ当用 C ?C++ ~程Ӟ~译器警告是非常重要的助手。ؓ了说明这一点,下面的例子包含一个微妙的错误Qؓ一个整数值错误地指定了一点数控制符‘%f’?/p>
#include <stdio.h>
int
main (void)
{
printf ("Two plus two is %f\n", 4);
return 0;
}
一眼看去该错误q不明显Q但是它可被~译器捕捉到Q只要启用了警告选项 -Wall?/p>
~译上面的程?#8216;bad.c’Q将得到如下的消息:
$ gcc -Wall bad.c -o bad
bad.c: In function 'main':
bad.c:6: warning: double format, different type arg (arg 2)
q表明文?‘bad.c’W?6 行中的格式字W串用法不正?strong style="color: black; background-color: #a0ffff">GCC
的消息Lh下面的格?文g?行号:消息。编译器寚w误与警告区别对待Q前者将L~译Q后者表明可能存在的问题但ƈ不阻止程序编译?/p>
本例中,Ҏ数值来_正确的格式控制符应该?%d?/p>
如果不启?-WallQ程序表面看h~译正常Q但是会产生不正的l果Q?/p>
$ gcc bad.c -o bad
$ ./bad
Two plus two is 2.585495
显而易见,开发程序时不检查警告是非常危险的。如果有函数使用不当Q将可能DE序崩溃或生错误的l果。开启编译器警告选项 -Wall 可捕?C ~程时的多数常见错误?/p>
~译多个源文?
一个源E序可以分成几个文g。这样便于编辑与理解Q尤其是E序非常大的时候。这也各部分独立编译成为可能?/p>
下面的例子中我们程?Hello World 分割?3 个文Ӟ‘main.c’Q?#8216;hello_fn.c’和头文g‘hello.h’。这是主E序‘main.c’Q?/p>
#include "hello.h"
int
main(void)
{
hello ("world");
return 0;
}
在先前的例子‘hello.c’中,我们调用的是库函?printfQ本例中我们用一个定义在文g‘hello_fn.c’中的函数 hello 取代它?/p>
ȝ序中包含有头文g‘hello.h’Q该头文件包含函?hello 的声明。我们不需要在‘main.c’文g中包含系l头文g‘stdio.h’来声明函?printfQ因?#8216;main.c’没有直接调用 printf?/p>
文g‘hello.h’中的声明只用了一行就指定了函?hello 的原型?/p>
void hello (const char * name);
函数 hello 的定义在文g‘hello_fn.c’中:
#include <stdio.h>
#include "hello.h"
void
hello (const char * name)
{
printf ("Hello, %s!\n", name);
}
语句 #include "FILE.h" ?#include <FILE.h> 有所不同Q前者在搜烦pȝ头文件目录之前将先在当前目录中搜索文?#8216;FILE.h’Q后者只搜烦pȝ头文件而不查看当前目录?/p>
要用gcc~译以上源文Ӟ使用下面的命令:
$ gcc -Wall main.c hello_fn.c -o newhello
本例中,我们使用选项 -o 为可执行文g指定了一个不同的名字 newhello。注意到头文?#8216;hello.h’q未在命令行中指定。源文g中的?#include "hello.h" 指示W得编译器自动其包含到合适的位置?/p>
要运行本E序Q输入可执行文g的\径名Q?/p>
$ ./newhello
Hello, world!
源程序各部分被编译ؓ单一的可执行文gQ它与我们先前的例子产生的结果相同?/p>
点击此处下蝲本节的操作视?/p>
单的 Makefile 文g
Z于不熟悉 make 的读者理解,本节提供一个简单的用法CZ。Make 凭借本w的优势Q可在所有的 Unix pȝ中被扑ֈ。要了解关于Gnu make 的更多信息,请参?Richard M. Stallman ?Roland McGrath ~写?GNU Make 手册?/p>
Make ?makefile(默认是当前目录下的名?#8216;Makefile’的文?中读取项目的描述。makefile指定了一pd目标Q比如可执行文gQ和依赖Q比如对象文件和源文Ӟ的编译规则,其格式如下:
目标Q?依赖
命o
Ҏ一个目标,make 查其对应的依赖文件修Ҏ间来定该目标是否需要利用对应的命o重新建立。注意到Qmakefile ?em>命o行必M单个?TAB 字符q行~进Q不能是I格?/p>
GNU Make 包含许多默认的规?参?em>隐含规则)来简?makefile 的构建。比如说Q它们指?#8216;.o’文g可以通过~译‘.c’文g得到Q可执行文g可以通过?#8216;.o’链接C赯得。隐含规则通过被叫?em>make变量的东西所指定Q比?CC(C 语言~译??CFLAGS(CE序的编译选项)Q在makefile文g中它们通过独占一行的 变量=?/strong> 的Ş式被讄。对 C++ Q其{h的变量是CXX?strong>CXXFLAGSQ而变?strong>CPPFLAGS
则是~译预处理选项?/p>
现在我们Z一节的目写一个简单的 makefile 文gQ?/p>
CC=gcc
CFLAGS=-Wall
hello: hello.o hello_fn.o
clean:
rm -f hello hello.o hello_fn.o
该文件可以这h读:使用 C 语言~译?gccQ和~译选项‘-Wall’,从对象文?#8216;hello.o’?#8216;hello_fn.o’生成目标可执行文?helloQ文?#8216;hello.o’?#8216;hello_fn.o’通过隐含规则分别?#8216;hello.c’?#8216;hello_fn.c’生成Q。目?strong>clean没有依赖文gQ它只是单地U除所有编译生成的文g?strong>rm命o的选项 ‘-f’(force) 抑制文g不存在时产生的错误消息?/p>
要用该 makefile 文gQ输?make。不加参数调用makeӞmakefile文g中的W一个目标被建立Q从而生成可执行文g‘hello’Q?/p>
$ make
gcc -Wall -c -o hello.o hello.c
gcc -Wall -c -o hello_fn.o hello_fn.c
gcc hello.o hello_fn.o -o hello
$ ./hello
Hello, world!
一个源文g被修改要重新生成可执行文Ӟ单地再次输入 make 卛_。通过查目标文件和依赖文g的时间戳Q程?make 可识别哪些文件已l修改ƈ依据对应的规则更新其对应的目标文Ӟ
$ vim hello.c (打开~辑器修改一下文?
$ make
gcc -Wall -c -o hello.o hello.c
gcc hello.o hello_fn.o -o hello
$ ./hello
Hello, world!
最后,我们U除 make 生成的文Ӟ输入 make cleanQ?/p>
$ make clean
rm -f hello hello.o hello_fn.o
一个专业的 makefile文g通常包含用于安装(make install)和测?make check){额外的目标?/p>
本文中涉及到的例子都_单以至于可以完全不需要makefileQ但是对M大些的程序都使用 make 是很有必要的?/p>
链接外部?
库是预编译的目标文g(object files)的集合,它们可被链接q程序。静态库以后~?#8216;.a’的特D的存档文g(archive file)存储?/p>
标准pȝ库可在目?/usr/lib ?/lib 中找到。比如,在类 Unix pȝ?C 语言的数学库一般存储ؓ文g /usr/lib/libm.a。该库中函数的原型声明在头文?/usr/include/math.h 中。C 标准库本w存储ؓ /usr/lib/libc.aQ它包含 ANSI/ISO C 标准指定的函敎ͼ比如‘printf’。对每一?C E序来说Qlibc.a 都默认被链接?/p>
下面的是一个调用数学库 libm.a ?sin 函数的的例子Q创建文?strong>calc.cQ?/p>
#include <math.h>
#include <stdio.h>
int
main (void)
{
double x = sin (2.0);
printf ("The value of sin(2.0) is %f\n", x);
return 0;
}
试单独从该文g生成一个可执行文g导致一个链接阶D늚错误Q?/p>
$ gcc -Wall calc.c -o calc
/tmp/ccbR6Ojm.o: In function 'main':
/tmp/ccbR6Ojm.o(.text+0x19): undefined reference to ‘sin’
函数 sinQ未在本E序中定义也不在默认?#8216;libc.a’中;除非被指定,~译器也不会链接‘libm.a’?/p>
Zɾ~译器能?sin 链接q主E序‘calc.c’Q我们需要提供数学库‘libm.a’。一个容易想C比较ȝ的做法是在命令行中显式地指定它:
$ gcc -Wall calc.c /usr/lib/libm.a -o calc
函数?#8216;libm.a’包含所有数学函数的目标文gQ比?strong>sin,cos,exp,log?strong>sqrt。链接器搜索所有文件来扑ֈ包含 sin 的目标文件?/p>
一旦包?sin 的目标文件被扑ֈQ主E序p被链接,一个完整的可执行文件就可生成了Q?/p>
$ ./calc
The value of sin(2.0) is 0.909297
可执行文件包含主E序的机器码以及函数?#8216;libm.a’?sin 对应的机器码?/p>
为避免在命o行中指定镉K的\径,~译器ؓ链接函数库提供了快捷的选项‘-l’。例如,下面的命?/p>
$ gcc -Wall calc.c -lm -o calc
与我们上面指定库全\?#8216;/usr/lib/libm.a’的命令等仗?/p>
一般来_选项 -lNAME佉K接器试链接pȝ库目录中的函数库文g libNAME.a。一个大型的E序通常要用很?-l 选项来指定要链接的数学库Q图形库Q网l库{?/p>
~译C++与Fortran
GCC ?GNU ~译器集合(GNU Compiler CollectionQ的首字母羃写词。GNU ~译器集合包?CQC++QObjective-CQFortranQJava ?Ada 的前端以及这些语a对应的库Qlibstdc++QlibgcjQ?#8230;…Q?/p>
前面我们只涉及到 C 语言Q那么如何用 gcc ~译其他语言呢?本节简单介l?C++ ?Fortran ~译的例子?/p>
首先我们试~译单的 C++ 的经典程?Hello worldQ?/p>
#include <iostream>
int main(int argc,char *argv[])
{
std::cout << "hello, world\n";
return 0;
}
文件保存ؓ‘hello.cpp’Q用 gcc ~译Q结果如下:
$ gcc -Wall hello.cpp -o hello
/tmp/cch6oUy9.o: In function `__static_initialization_and_destruction_0(int, int)':
hello.cpp:(.text+0x23): undefined reference to `std::ios_base::Init::Init()'
/tmp/cch6oUy9.o: In function `__tcf_0':
hello.cpp:(.text+0x6c): undefined reference to `std::ios_base::Init::~Init()'
/tmp/cch6oUy9.o: In function `main':
hello.cpp:(.text+0x8e): undefined reference to `std::cout'
hello.cpp:(.text+0x93): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<<
<std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'
/tmp/cch6oUy9.o:(.eh_frame+0x11): undefined reference to `__gxx_personality_v0'
collect2: ld returned 1 exit status
出错了!Q而且错误q很多,很难看懂Q这可怎么办呢Q在解释之前Q我们先试试下面的命令:
$ gcc -Wall hello.cpp -o hello -lstdc++
噫,加上-lstdc++选项后,~译竟然通过了,而且没有M警告。运行程序,l果如下Q?/p>
$ ./hello
hello, world
通过上节Q我们可以知道,-lstdc++ 选项用来通知链接器链接静态库 libstdc++.a。而从字面上可以看出,libstdc++.a 是C++ 的标准库Q这样一来,上面的问题我们就不难理解了──~译 C++ E序Q需要链?C++ 的函数库 libstdc++.a?/p>
~译 C 的时候我们不需要指?C 的函数库Qؓ什?C++ 要指定呢Q这是由于早?gcc 是指 GNU ?C 语言~译器(GNU C CompilerQ,随着 C++QFortran {语a的加入,gcc的含义才变化成了 GNU ~译器集合(GNU Compiler CollectionQ。C作ؓ gcc 的原生语aQ故~译时不需额外的选项?/p>
不过q运的是Q?strong style="color: black; background-color: #a0ffff">GCC 包含专门?C++ 、Fortran {语a的编译器前端。于是,上面的例子,我们可以直接用如下命令编译:
$ g++ -Wall hello.cpp -o hello
GCC ?C++ 前端?g++Q?Fortran 的情况则有点复杂Q在 gcc-4.0 版本之前QFortran 前端?g77Q?strong style="color: black; background-color: #a0ffff">gcc-4.0之后的版本对应的 Fortran 前端则改?gfortran。下面我们先写一个简单的 Fortran CZE序Q?/p>
C Fortran CZE序
PROGRAM HELLOWORLD
WRITE(*,10)
10 FORMAT('hello, world')
END PROGRAM HELLOWORLD
文件保?#8216;hello.f’Q用 GCC ?Fortran 前端~译q行该文?/p>
$ gfortran -Wall hello.f -o hello
$ ./hello
hello, world
我们已经知道Q直接用 gcc 来编?C++ Ӟ需要链?C++ 标准库,那么?gcc ~译 FortranӞ命o该怎么写呢Q?/p>
$ gcc -Wall hello.f -o helloworld -lgfortran -lgfortranbegin
注意Q上面这条命令与 gfortran 前端是等LQg77 与此E有不同Q。其中库文g libgfortranbegin.a (通过命o行选项 -lgfortranbegin 被调? 包含q行和终止一?Fortran E序所必须的开始和退Z码。库文g libgfortran.a 包含 Fortran 底层的输入输出等所需要的q行函数?/p>
对于 g77 来说Q下面两条命令是{h的(注意?g77 对应?gcc ?4.0 之前的版本)Q?/p>
$ g77 -Wall hello.f -o hello
$ gcc-3.4 -Wall hello.f -o hello -lfrtbegin -lg2c
命o行中的两个库文g分别包含 Fortran 的开始和退Z码以?Fortran 底层的运行函数?/p>

]]>