Linux之GCC经典教程之一.docx
《Linux之GCC经典教程之一.docx》由会员分享,可在线阅读,更多相关《Linux之GCC经典教程之一.docx(8页珍藏版)》请在冰豆网上搜索。
Linux之GCC经典教程之一
Linux之GCC经典教程
准备工作
注意:
本文可能会让你失望,如果你有下列疑问的话:
为什么要在终端输命令啊?
GCC是什么东西,怎么在菜单中找不到?
GCC不能有像VC那样的窗口吗?
……那么你真正想要了解的可能是anjuta,kdevelop,geany,codeblocks,eclipse,netbeans等IDE集成开发环境。
即使在这种情况下,由于GCC是以上IDE的后台的编译器,本文仍值得你稍作了解。
如果你还没装编译环境或自己不确定装没装,不妨先执行
sudoapt-getinstallbuild-essential
如果你需要编译Fortran程序,那么还需要安装gfortran(或g77)
sudoapt-getinstallgfortran
编译简单的C程序
C语言经典的入门例子是HelloWorld,下面是一示例代码:
#include
intmain(void)
{
printf(“Hello,world!
n”);
return0;
}
我们假定该代码存为文件‘hello.c’。
要用gcc编译该文件,使用下面的命令:
$gcc-g-Wallhello.c-ohello
该命令将文件‘hello.c’中的代码编译为机器码并存储在可执行文件‘hello’中。
机器码的文件名是通过-o选项指定的。
该选项通常作为命令行中的最后一个参数。
如果被省略,输出文件默认为‘a.out’。
注意到如果当前目录中与可执行文件重名的文件已经存在,它将被覆盖。
选项-Wall开启编译器几乎所有常用的警告──强烈建议你始终使用该选项。
编译器有很多其他的警告选项,但-Wall是最常用的。
默认情况下GCC不会产生任何警告信息。
当编写C或C++程序时编译器警告非常有助于检测程序存在的问题。
注意如果有用到math.h库等非gcc默认调用的标准库,请使用-lm参数
本例中,编译器使用了-Wall选项而没产生任何警告,因为示例程序是完全合法的。
选项“”-g”"表示在生成的目标文件中带调试信息,调试信息可以在程序异常中止产生core后,帮助分析错误产生的源头,包括产生错误的文件名和行号等非常多有用的信息。
要运行该程序,输入可执行文件的路径如下:
$./hello
Hello,world!
这将可执行文件载入内存,并使CPU开始执行其包含的指令。
路径./指代当前目录,因此./hello载入并执行当前目录下的可执行文件‘hello’。
捕捉错误
如上所述,当用C或C++编程时,编译器警告是非常重要的助手。
为了说明这一点,下面的例子包含一个微妙的错误:
为一个整数值错误地指定了一浮点数控制符‘%f’。
#include
intmain(void)
{
printf(“Twoplustwois%fn”,4);
return0;
}
一眼看去该错误并不明显,但是它可被编译器捕捉到,只要启用了警告选项-Wall。
编译上面的程序‘bad.c’,将得到如下的消息:
$gcc-Wall-obadbad.c
main.c:
在函数‘main’中:
main.c:
5:
警告:
格式‘%f’需要类型‘double’,但实参2的类型为‘int’
这表明文件‘bad.c’第6行中的格式字符串用法不正确。
GCC的消息总是具有下面的格式文件名:
行号:
消息。
编译器对错误与警告区别对待,前者将阻止编译,后者表明可能存在的问题但并不阻止程序编译。
本例中,对整数值来说,正确的格式控制符应该是%d。
如果不启用-Wall,程序表面看起来编译正常,但是会产生不正确的结果:
$gccbad.c-obad
$./bad
Twoplustwois0.000000
显而易见,开发程序时不检查警告是非常危险的。
如果有函数使用不当,将可能导致程序崩溃或产生错误的结果。
开启编译器警告选项-Wall可捕捉C编程时的多数常见错误。
编译多个源文件
一个源程序可以分成几个文件。
这样便于编辑与理解,尤其是程序非常大的时候。
这也使各部分独立编译成为可能。
下面的例子中我们将程序HelloWorld分割成3个文件:
‘hello.c’,‘hello_fn.c’和头文件‘hello.h’。
这是主程序‘hello.c’:
#include“hello.h”
intmain(void)
{
hello(“world”);
return0;
}
在先前例子的‘hello.c’中,我们调用的是库函数printf,本例中我们用一个定义在文件‘hello_fn.c’中的函数hello取代它。
主程序中包含有头文件‘hello.h’,该头文件包含函数hello的声明。
我们不需要在‘hello.c’文件中包含系统头文件‘stdio.h’来声明函数printf,因为‘hello.c’没有直接调用printf。
文件‘hello.h’中的声明只用了一行就指定了函数hello的原型。
voidhello(constchar*name);
函数hello的定义在文件‘hello_fn.c’中:
#include
#include“hello.h”
voidhello(constchar*name)
{
printf(“Hello,%s!
n”,name);
}
语句#include“FILE.h”与#include有所不同:
前者在搜索系统头文件目录之前将先在当前目录中搜索文件‘FILE.h’,后者只搜索系统头文件而不查看当前目录。
要用gcc编译以上源文件,使用下面的命令:
$gcc-Wallhello.chello_fn.c-onewhello
本例中,我们使用选项-o为可执行文件指定了一个不同的名字newhello。
注意到头文件‘hello.h’并未在命令行中指定。
源文件中的的#include“hello.h”指示符使得编译器自动将其包含到合适的位置。
要运行本程序,输入可执行文件的路径名:
$./newhello
Hello,world!
源程序各部分被编译为单一的可执行文件,它与我们先前的例子产生的结果相同。
简单的Makefile文件
为便于不熟悉make的读者理解,本节提供一个简单的用法示例。
Make凭借本身的优势,可在所有的Unix系统中被找到。
要了解关于Gnumake的更多信息,请参考RichardM.Stallman和RolandMcGrath编写的GNUMake手册。
Make从makefile(默认是当前目录下的名为‘Makefile’的文件)中读取项目的描述。
makefile指定了一系列目标(比如可执行文件)和依赖(比如对象文件和源文件)的编译规则,其格式如下:
目标:
依赖
命令
对每一个目标,make检查其对应的依赖文件修改时间来确定该目标是否需要利用对应的命令重新建立。
注意到,makefile中命令行必须以单个的TAB字符进行缩进,不能是空格。
GNUMake包含许多默认的规则(参考隐含规则)来简化makefile的构建。
比如说,它们指定‘.o’文件可以通过编译‘.c’文件得到,可执行文件可以通过将‘.o’链接到一起获得。
隐含规则通过被叫做make变量的东西所指定,比如CC(C语言编译器)和CFLAGS(C程序的编译选项);在makefile文件中它们通过独占一行的变量=值的形式被设置。
对C++,其等价的变量是CXX和CXXFLAGS,而变量CPPFLAGS则是编译预处理选项。
现在我们为上一节的项目写一个简单的makefile文件:
CC=gcc
CFLAGS=-Wall
hello:
hello.ohello_fn.o
clean:
rm-fhellohello.ohello_fn.o
该文件可以这样来读:
使用C语言编译器gcc,和编译选项‘-Wall’,从对象文件‘hello.o’和‘hello_fn.o’生成目标可执行文件hello(文件‘hello.o’和‘hello_fn.o’通过隐含规则分别由‘hello.c’和‘hello_fn.c’生成)。
目标clean没有依赖文件,它只是简单地移除所有编译生成的文件。
rm命令的选项‘-f’(force)抑制文件不存在时产生的错误消息。
另外,需要注意的是,如果包含main函数的cpp文件为A.cpp,makefile中最好把可执行文件名也写成A。
要使用该makefile文件,输入make。
不加参数调用make时,makefile文件中的第一个目标被建立,从而生成可执行文件‘hello’:
$make
gcc-Wall-c-ohello.ohello.c
gcc-Wall-c-ohello_fn.ohello_fn.c
gcchello.ohello_fn.o-ohello
$./hello
Hello,world!
一个源文件被修改要重新生成可执行文件,简单地再次输入make即可。
通过检查目标文件和依赖文件的时间戳,程序make可识别哪些文件已经修改并依据对应的规则更新其对应的目标文件:
$vimhello.c(打开编辑器修改一下文件)
$make
gcc-Wall-c-ohello.ohello.c
gcchello.ohello_fn.o-ohello
$./hello
Hello,world!
最后,我们移除make生成的文件,输入makeclean:
$makeclean
rm-fhellohello.ohello_fn.o
一个专业的makefile文件通常包含用于安装(makeinstall)和测试(makecheck)等额外的目标。
本文中涉及到的例子都足够简单以至于可以完全不需要makefile,但是对任何大些的程序都使用make是很有必要的。
链接外部库
库是预编译的目标文件(objectfiles)的集合,它们可被链接进程序。
静态库以后缀为‘.a’的特殊的存档文件(archivefile)存储。
标准系统库可在目录/usr/lib与/lib中找到。
比如,在类Unix系统中C语言的数学库一般存储为文件/usr/lib/libm.a。
该库中函数的原型声明在头文件/usr/include/math.h中。
C标准库本身存储为/usr/lib/libc.a,它包含ANSI/ISOC标准指定的函数,比如‘printf’。
对每一个C程序来说,libc.a都默认被链接。
下面的是一个调用数学库libm.a中sin函数的的例子,创建文件calc.c:
#include
#include
intmain(void)
{
doublex=2.0;
doubley=sin(x);
printf(“Thevalueofsin(2.0)is%fn”,y);
return0;
}
尝试单独从该文件生成一个可执行文件将导致一个链接阶段的错误:
$gcc-Wallcalc.c-ocalc
/tmp/ccbR6Ojm.o:
Infunction