今天偶然看到这篇文章,作个入门了解仍是不错的。java
前一阵子在QQ上和朋友聊天的时候,总会看到有人说Linux上的应用程序开发是高手才能够完成的,并且这种“迷信”在目前彷佛还很广泛。然而,状况并非这样的,从程序库的支持方面,Linux平台为用户级应用程序的开发提供了不少功能强大且丰富的程序库,并且它们大部分是跨平台的(Boost、OpenGL、STL、Qt、Java等)和基于POSIX标准的(glibc等),同时Linux内核还为驱动程序的开发提供了功能完备的内核接口,从开发工具方面,Linux提供了功能强大的编译器GCC和调试器GDB,借助它们的帮助,咱们能够很轻松的在Linu x上开发出可移植性的应用程序。既然如此,“迷信”又源于何来呢?我想,一方面因为详细介绍Linux各类开发的书籍较少,各类Linux应用在国内仍不普及,另外一方面则是因为不少人在安装好一个Linux后,苦于找不到一个驾轻就熟的IDE环境,从而感到不知所措,毕竟,咱们不少人都习惯了写好程序后,按下F5,剩下的任务就让IDE全权代理了。其实想在Linux下如此这般固然也没问题。既然说到了IDE,就让咱们从它开始吧,相信选择一个好的IDE环境是你整个学习过程的一个不错的开始。c++
工欲善其事 必先利其器——IDE篇程序员
其实Linux下有许多功能强大的IDE环境,由于从某种意义上说,Linux是专为开发者准备的操做系统,这个东西固然少不了,在这里为读者介绍一些比较经常使用的IDE。编程
KDevelopeclipse
这是一个用Qt开发的IDE,其主要支持的语言是C / C++,socket
Eclipseide
近年来,eclipse能够说发展极为迅速,它不只是一个以java为主的开发平台,其功能强大的插件体系结构使得它能够被看成各类应用程序来使用。做为各类插件的载体,eclipse提供了完整的GUI接口,用户彻底能够借助eclipse来只关心本身想作的工做。函数
Emacs工具
VIMoop
山高月晓 水落石出——IDE后台的故事 GCC篇
前面咱们简要介绍了一些IDE环境,其中全部C/C++相关程序的编译都是由GCC来完成的,而IDE只不过起到了一个收集编译信息和为咱们的项目生成makefile等做用(后面咱们会提到)。出于目前Linux开发的特色,C还是系统开发的主流语言。因此,对GCC有一个全面的了解是颇有必要的,一旦IDE不能知足你的需求,咱们要有手工打造程序的能力,并且出于学习的目的,咱们每每不须要IDE生成的那些复杂的文件,为一个Hello world生成2M多的文件显然是多余的。
GCC的全称是GNU Compiler Collection,从这个名字咱们不难看出,GCC表明着一个编译器的集合,目前GCC能够支持C, C++, Objective-C, Objective-C++, Fortran, Java, and Ada等语言。可是出于通常性考虑,咱们这里只讨论GCC中的C/C++部分。
目前GCC的最新发布版是4.0.0,可是这个版本因为使用了新技术和新的编码规范,不少旧的代码都须要修改才能够经过编译,因此并不推荐使用这个版本。而相对稳定的新版本目前是3.4.4,你们能够到GNU的主页上更新下载。那么究竟GCC强大在哪里,如何使用?下面我就经过几个简单而实际的例子带你看看GCC提供的强大功能。
经过Helloworld的编译熟悉GCC的基本使用方法
彷佛为全部新语言提供一个Hello World样本程序已经成为了一种不成文的标准,人们经过它来认识语言的一些基本要素。在这里,咱们使用一个Hello World来看看如何用GCC生成可执行文件。
1 #include<stdio.h> 2 3 int main() 4 { 5 printf("hello world!\r\n"); 6 return 0; 7 }
把上面的文件存成helloworld.c,以后打开控制台,输入以下的命令
gcc helloworld.c –o helloworld
若是一切正常的话,你的控制台上应该没有任何输出。用ls查看你的工做目录,你会发现目录下多了一个名为helloworld的可执行文件,以后,执行
./hellworld
就会看到这个程序的输出了
很简单不是吗?可是学过计算机的朋友都应该知道,程序的编译过程要分为下图所示的过程而GCC的强大之处就在于它容许你在上面所示的任何一个过程当中停下来查看中间结果,并对其加以控制。
1. 预处理
首先是预处理过程,GCC的-E选项可让GCC在预处理后中止编译,并向标准输出打印预处理事后的文件。下面的-o用于指定输出文件的文件名。
gcc –E hellowrold.c –o helloworld.cpp
下面是helloworld.cpp的一部分的内容,咱们看到,文件已经包含了stdio.h中的内容。
若是咱们想执行下一步的编译过程,能够继续使用GCC的-x <language type>选项,该选项用于显示指定文件的后缀名(而不是让编译器根据后缀来自行判断)。咱们比较经常使用的language type有以下几种,(若是读者想得到更为完整参数说名,请参考GCC manual):
l c c-header c-cpp-output
l c++ c++-header c++-cpp-output
l assembler assembler-with-cpp
另外,下表列出了经常使用的GCC后缀名
文件后缀 |
注释 |
.c |
须要通过预处理的C代码文件 |
.i |
不须要通过预处理的C代码文件 |
.ii |
不须要通过预处理的C++代码文件 |
.h |
须要被预编译的C, C++, Objective-C头文件 |
.cc .cp .cxx .cpp .CPP .c++ .C |
须要被预处理的C++程序文件 |
.hh .H |
须要被预编译的C++头文件 |
.s |
汇编代码文件 |
.S |
须要被预处理的汇编文件 |
固然,你也能够省略掉language type的部分,这时候GCC会根据文件的后缀名自行判断,就像你没有使用该选项同样。
下面继续咱们的编译过程
2. 编译
若是咱们想得到编译后的源文件可使用-S选项,该选项让gcc只执行编译(生成汇编文件)而不进行汇编(生成目标文件),此时,咱们能够用-o选项指定输出的汇编文件的名称。
gcc –S helloworld.cpp –o hellowrld.S
3. 汇编
另外,咱们还可使用GCC的-c选项来编译和汇编源文件而不连接,此时-o指定的输出文件就是编译后的目标文件名
gcc –x c++ -c helloworld.cpp –o helloworld.o
4. 连接
最后,咱们能够利用GCC来把咱们刚才生成的.o文件连接成可执行程序
gcc helloworld.o –o helloworld
这一次,咱们使用了-o选项指定了可执行文件名,也就是说,根据输入文件类型的不一样,-o有着不一样的含义。
5. 函数库的连接和包含文件
对于咱们编写的任和一个程序,没有库函数的支持是不可想象的,而当咱们要使用的头文件和函数库不在GCC默认的搜索路径下的时候(例如OpenGL、Qt、KDE、Boost等),咱们就须要手工来告诉GCC他们的位置。
先来看头文件路径的指定。咱们能够利用-I<dir_name>来指定咱们但愿GCC去搜索的头文件目录,例如咱们要使用X11的程序,咱们就要使用下面的选项
再来看库函数的设置:咱们经过-L<dir_name>和-l<lib_name>两个命令行选项完成任务。其中-L用于告诉GCC在<dir_name>中去寻找函数库,而-l选项则告诉GCC使用用户指定的程序库。在Linux中,函数库的命名是遵循UNIX约定的,即lib{lib name},例如libsocket.so,因此当你须要告诉GCC使用这些库的时候,你就可使用-lsocket选项。一般,这两个命令是结合在一块儿使用的,例如引用X11程序库的时候,咱们能够这样:
–L/usr/X11R6/lib –lX11
另外,GCC在默认状况下使用共享库来连接程序,而当你想连接静态库的时候,必定要使用-static选项,例如-lncurses -static
在这一部分的最后,咱们对编译时用到的GCC经常使用命令作一个简要的总结
命令 |
说明 |
-x <language type> |
显示指定输入文件的格式 |
-c |
编译和汇编源文件,但不连接,输出为.o文件格式 |
-S |
编译源文件,但不汇编,输出为.S文件格式 |
-E |
只对源文件进行预处理,并不编译,输出为通过预处理的源代码 |
咱们能够利用上面的-x和-c / –S / –E的组合来控制GCC的整个编译过程,其中-x用于告诉GCC咱们从哪里开始,而-c / -S / -E用来告诉GCC在那里结束。 |
|
-o output-file |
用来指定输出文件,该选项能够指定不少种输出文件,例如:可执行文件、目标文件、汇编文件或者是预处理过的程序代码等,这要根据具体的命令行参数而定。固然,GCC还提供了默认的-o选项,其中,默认的可执行文件是a.out,目标文件是<file_name>.o,汇编文件是<file_name>.s,预编译头文件的格式是<file_name>.suffix.gch |
-I<dir name> |
告诉GCC在<dir name>中去寻找头文件 |
-L<dir name> |
告诉GCC在<dir name>中去寻找库文件 |
-l<lib name> |
使用名为lib<lib name>.so的程序库 |
-static |
通知GCC连接静态库 |
上面,咱们提到了关于GCC编译的经常使用命令,这里另外补充一些帮助性的经常使用命令,他们可让你对GCC的基本配置和使用做一个了解。
命令 |
说明 |
-v |
向标准错误打印编译GCC时使用的命令和预处理器和编译器的编本,若是你在升级GCC时犹豫不定,那么不妨在你的控制台上使用这个选项,看看厂商的配置 |
--help |
向标准输出打印GCC命令行选项的描述。若是把这个命令和-v结合起来,--help则会同时打印被GCC调用的进程的命令行描述。若是把-Wextra和—help结合起来,那么,那些没有文档描述的命令行选项也会被显示出来。 |
--target-help |
向标准输出打印每个工具的特定命令行选项的描述 |
--version |
现实GCC的版本和版权信息 |
在这部分的最后,咱们来谈一谈关于构建软件时连接参数的设定问题。在上面的第5部分咱们已经提到了,函数库的使用是须要-L和-l一块儿配合来使用的,但实际上,每每一个像样的程序须要不少库的支持,例如,若是你须要编写一个GTK程序,咱们须要下面的连接参数:
-L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic -lgmodule -lglib -ldl -lXi -lXext -lX11 –lm,看上去有些吓人,你可能会问,我如何知道须要这些呢,若是我想编写KDE的程序呢,还有OpenGL呢?其实,状况比你想象的要好不少,在/usr/bin目录下,有不少名为xxx-config的脚本,它们的做用就是向用户显示编译连接程序时使用的参数的。这些脚本能够接受一些参数,比较经常使用的有—libs用于列出连接特定程序时使用的程序库,另外--cflags用于生成头文件的包含目录,也就是上面咱们提到的-I参数。因而,对于GTK程序,咱们可使用下面的命令来编译:
gcc gtksource.c `gtk-config –libs --cflags`
固然,为每一种程序写一个config显然不是一个好办法,目前新的开发包都使用pkg-config这个脚原本生成连接参数。你可使用pkg-config –list-all查看pkg-config支持的全部连接参数
当你在上面这份列表中查到了本身想要程序包时,就可使用下面的命令来编译程序了
gcc <source file>.suffix `pkg-config <pkg name> --libs --cflags`
让GCC帮助你更好的工做
上面咱们简单介绍了GCC的经常使用命令行选项,其实GCC的功能比上面提到的那些要丰富得多,GCC对代码的警告、优化、调试等方面提供了丰富的支持,下面咱们就从一些例子来看看GCC提供的这些功能。
1. 对问题代码提出警告
GCC对程序代码提供了完整的检查功能,因为C/C++语言自己的特色,不少错误都是程序员无心间犯下的,例如使用了未定义的变量、在bool表达式中使用了=而不是==等等问题,利用GCC提供的代码检查功能,咱们可让编译器为咱们找到这些问题,避免运行时发生灾难。
首先,咱们来看一个“问题代码”
1 /* test_warning.c We use this file to check the warning facilities provided by GCC*/ 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 7 void main() { /* main should return int*/ 8 9 int a, b; 10 11 long long l = 2.2; /* long long type is GNU extension, not standard ANSI / ISO type*/ 12 13 miss_decl(); /* We call an undeclared function*/ 14 15 if (a = 0) /* May be we want == here instead of =*/ 16 17 printf (“a really equals to 0?/n”); 18 19 if (b != 0) /* We used uninitialized variables*/ 20 21 /* %d and “We should put b here” don’t match*/ 22 23 printf(“We make a mistake again! b = %d/n”, “We should put b here”); 24 25 }; 26 27 28 void miss_decl() { 29 30 /* /* This type of annotation is prohibited*/ 31 32 printf(“We should put the declaration before it’s been used!/n”); 33 34 }
上面这些代码故意制造了不少编程中出现的常见问题,接下来,咱们就用这段代码来检测一下GCC提供的各类经常使用的警告设施。
首先,咱们不使用任何警告设施编译上面的程序
gcc test_warning.c –o test_warning
默认状况下,GCC会给出输出,其中GCC识别出了main函数不标准(warning)以及使用了未声明的函数(error)两个问题,可是其余的GCC并未察觉。
1. 利用-pedantic找出不符合ANSI / ISO标准的代码
执行下面的命令:gcc –pedantic test_warning.c –o test_warning
能够看到,此次GCC以警告的形式报告了代码中long long的使用,可是要说明的是咱们并不能依赖这个选项来保证咱们的代码彻底符合ANSI / ISO标准,由于该选项只报告ANSI C要求编译器进行检察的内容。另外,你还可使用-pedantic-errors让GCC把全部的警告都变成错误。
2. 利用-Wformat检查printf中的参数不匹配问题
执行下面的命令:gcc –Wformat test_warning.c –o test_warning
3. 利用-WComment找出注释中的错误
执行下面的命令:gcc –WComment test_warning.c –o test_warning
4. 利用-Wparentheses查找bool表达式中的=错误
执行下面的命令:gcc –Wparentheses test_warning.c –o test_warning
5. 用-Wuninitialized查找未初始化变量的使用
执行下面的命令:gcc –O –Wuninitialized test_warning.c –o test_warning
值得说明的是,在使用这个选项的时候,必定要配合上-O(后面咱们会提到)选项
6. 利用-Wimplicit-function-declaration / -Werror-implicit-function-declaration检查未声明函数的使用
执行下面的命令:gcc -Wimplicit-function-declaration test_warning.c –o test_warning
另外-Werror-implicit-function-declaration和-Wimplicit-function-declaration做用是相似的,只是若是你使用了未声明的函数,前者会把它认为是一个错误。
7. 若是你只是想对你的代码进行全面的检查,你大可没必要把上面的选项一并列出来,GCC提供了-Wall选项,含义就是列出全部代码中的警告
执行下面的命令:gcc –Wall test_warning.c –o test_warning
8. 若是你想走另外一个极端,也就是不想让gcc输出任何警告,那么使用-w选项,该选项禁止全部的警告
执行下面的命令:gcc –w test_warning.c –o test_warnin
<输出结果>
对于上面全部的选项,你均可以把它们和-Werror选项一块儿使用,这样就能够把全部的警告都变成错误。另外,若是你只是想对代码进行检查而并不执行编译的话,可以使用-fsyntax-only选项,像下面的命令这样
gcc –fsyntax-only test_warning.c
基本上来讲,咱们经常使用的一些警告选项就是这些,而其中-Wall更是咱们极为经常使用的功能。
2. 优化选项
这一部分的内容能够分红两部分,一部分是让编译器对代码进行分析后,进行的代码优化,另外一部分是咱们能够为编译器制定一些关于硬件的信息,让他生成对硬件结合的更好的代码,而咱们之因此要用源代码来编译程序,不少状况下,是出于这方面的缘由。
首先来看代码优化,从代码的总体优化上,GCC提供了下面的选项
-O –O1
这两个选项的含义是同样的,GCC将执行减小代码尺寸和执行时间的优化,对于那些会严重影响编译时间的优化选项,这个级别的优化并不会执行。
-O2
在这一级别GCC将会提供全部支持的优化,但这其中并不包括以空间换时间的优化手段,例如编译器不会使用循环展开和函数内联。和-O相比,该选项进一步加快了编译时间和生成代码的性能。
-O3
除了-O2提供的优化选项外,还指定了-finline-functions,-funswitch-loops和-fgcse-afer-reload选项,目的只有一个就是全力执行代码优化。
-Os
这个选项是专门用来优化代码尺寸的,-Os打开了全部-O2级别中不会显著增加代码尺寸的优化选项
-O0
该选项表明不执行优化
在这里要说明的是,尽管GCC提供了1~3和s这4个总体优化选项,但从实际的优化效果上来看,每每O3优化出来的程序的效率并非最高的,而大部分状况下咱们都在使用-O2,若是你但愿得到最高的效率利益,那么不妨这4个选项都试试。另外,其实这些选项只不过是GCC提供的不少单方面优化的一个组合,若是你想了解更为具体的优化内容,能够去查看GCC手册,出于篇幅限制,这里不细谈了。最后要记住的一点是,若是你的程序是用于高精度数值计算的,那么记住不要使用上面任何的优化选项。
下面来看基于硬件优化,因为这部分和计算机硬件相关,这里仅用Intel的CPU作一些说明:
对于全部为Intel和AMD x86-64提供的优化选项都是用m开头的,下面写一些经常使用的选项:
-march
该选项用来指定CPU的类型,经常使用的有i386 / i486 / i586 / pentium-mmx / i686 / pentium2 / pentium3 / pentium-m / pentium4 / prescott / k6 / athlon / athlon-4 / k8等等,读者能够根据本身的状况进行指定。
-mfpmath
该选项用于指定浮点运算单元的类型。包括
387
使用标准的数学协处理器
sse
使用SSE指令集提供的标量浮点运算。在Pentium3 / Athlon-4以及更新的芯片上支持这个特性。另外,在pentium4以及AMD x86-64处理器上,SSE2还能够进行双精度浮点计算。
sse,387
混合使用387数学协处理器和SSE指令集,该选项能够充分的利用CPU的浮点寄存器和xmm寄存器,可是该选项还处在试验阶段。
-malign-double
该选项使得GCC把double / long double / long long类型的变量在4字节或2字节地址上对齐,
在Pentium级的CPU上,这会使得代码的执行速度更快,固然带来的代价是须要更多的内存来执行程序。-mmmx –msse –msse2 –msse3 –m3dnow
这些选项用来启动内置函数直接使用这些处理器扩展指令的功能。在编译3D或多媒体程序的时候,使用他们是很是有效的。
3. 对调试的支持
当程序出错的时候,咱们能够在Visual Studio中轻松的进行调试,而在Linux中,一旦出现Segmentation Fault,彷佛咱们除了用眼睛去看代码就没有更好的选择了,其实状况否则,用GCC向程序加入一些适当的调试信息,咱们能够利用GDB去调试程序。在这里,咱们介绍最为经常使用的-g和-ggdb选项。
先来看-g。该选项能够利用操做系统的“原生格式(native format)”生成调试信息。GDB能够直接利用这个信息。尽管咱们能够把-O和-g放在一块儿使用,可是,这种作法是极为不推荐的。
若是你想用GDB来调试程序,那么你可使用-ggdb来让GCC为GDB生成更为丰富的调试信息,可是,此时你就不能用其余的调试器来进行调试了。
最后要说明的是,上面这两个选项均可以接受一个输出调试信息的级别,默认的级别是2。若是你指定1级(-g1),那么GCC会生成最少的调试信息,这包括函数和全局变量的描述信息,可是对于局部变量和行号等信息,在这个级别是不会输出的。另一个级别是3级(-g3),在这一级别上,GCC会为程序中的全部宏定义和符号生成调试信息。
小结
经过这篇文章,但愿能过对想学习Linux开发中用到的一些基本的技术和知识有一个了解,而且可以本身动手开始作些试验性的工做,其实,这里还有不少问题没有谈到,例如利用GDB进行调试、利用make管理工程、利用autoconf为程序生成配置脚本、利用CVS管理程序源文件等等,这些问题有待在从此的文章中和读者一块儿交流。