gcc编译器
gcc作为linux平台下的标准C编译器,功能强大;
gcc test1.c
注:旧版本gcc编译器编译时会出现警告信息,最新版本gcc4.4默认不会提示,加-Wall参数后会提示警告信息,gcc仍能完成编译程序;编译完成之后gcc会创建一个可执行文件 a.out,并执行;
./a.out
使用选项-o来改变编译后的文件名,如
gcc -o test1 test1.c
./test1
gcc选项概述
gcc命令完整格式:
gcc [options] [filename]
命令行按编译选项(参数)指定的操作对给定的文件(filename)进行编译处理。在 gcc后面可有多个编译选项,同时进行多个编译操作。
选项 | 描述 |
-x language | C 、C++、汇编 |
-c | 只编译和汇编,不连接 |
-S | 编译,不汇编和连接 |
-E | 预处理,不编译、汇编、连接 |
-o file1 file2 | 将file2编译成可执行文件file1 |
-L library | 指定所使用的库文件 |
-I directory | include文件搜索指定目录 |
-w | 禁止警告信息 |
-Wall | 显示附加警告信息 |
-g | 显示排错信息以便于调试 |
-pg | 产生gprof所需信息 |
-p | 产生prof所需信息 |
-O | 优化编译出的代码 |
-O2 | 进行比-O高级一级的优化 |
-O3 | 产生更高级的优化 |
-v | 显示gcc版本 |
-m*** | 优化不同的微处理器 |
-pedantic | 严格要求符合ANSI标准 |
选项使用介绍
-c
gcc只把源码(.c)编译成目标代码(.o),跳过连接一步;能使编译多个C程序时的速度更快且更加容易管理,此选项默认时,gcc建立目标代码文件只有一个.o的扩展名。
gcc -c test.c
-S
gcc在C程序文件产生了汇编语言文件后停止编译,汇编语言文件的默认扩展名为.s
gcc -S test.c
-E
指示编译器只对输入的文件进行预处理,且预处理的输出将被送到标准输出而不是存储在文件里。
-v
得到gcc版本相关信息,确定所用的是ELF还是a.out
两者都是二进制格式,ELF格式增长的灵活性超过了a.out,而且ELF格式设置共享程序库上拥有更大的便利性。
-m***
它是一类优化编译的选项,其中***代表的是用户所用的中央处理器的型号,如-m486告诉gcc把正在编译的程序视作转为486微处理器所编写的,当它和用户机器的中央处理器型号正好匹配时,将可取得最佳的优化效果。
调试标记
使用调试符号来编译程序,gcc在目标文件(.o)和创建可执行文件中插入额外信息,使得gdb能够判断编译过的代码和程序源码之间的关系,没有该信息gdb将无法判断源程序中的哪行代码在被执行。在默认情况下,调试符号不会编译到程序中,这会增大可执行文件的体积,调试之后,不用重新编译程序。
调试符号的使用可能与优化不兼容,两者混用,可能导致调试的混乱和失败。在编译一段写好的代码时,尽量避免使用-o选项和优化开关-f选项。可使用gcc的-g选项来产生调试符号,是一个默认的选项:
gcc -g -Wall -o test test.c
使用gdb调试器,ggdb3告诉gcc,使用gdb的扩展产生调试符号。其中“3”表示使用三级(最高级)调试信息,命令
gcc -ggdb3 -Wall -o test test.c
调试符号示例
#include <stdio.h>
int main(void){
int input = 0;
printf("Enter an integer: ");
scanf("%d",&input);
printf("Twice the number you supplied is %d\n", 2*input);
return 0;
}
编译程序
gcc -Wall -o test2 test2.c
./test2
得到如下输出:
Enter an integer:5
Segmentation fault
使用调试符号来编译他
gcc -ggdb3 -Wall -o test2 test2.c
编译允许使用储存信息,可在bash中使用如下命令来指定core文件大小的最大值,该命令表示无大小限制。
ulimit -c unlinmited
得到输出
Enter an integer:5
Segmentation fault(core dumped)
输出错误相同,多了一个core.***文件,****为进程编号(编译后的程序都是被装载到进程中执行的),该文件告诉你到底错在哪。
将加载程序和core文件到gdb分析
gdb test2 core.***
program teminated with signal 11,Segmentation fault.
[New process 9868]
#0 0x004cb1c0 in_IO_vfscanf_internal () form /lib/libc.so.6
(gdb)
进入gdb调试环境,输入run或者r命令,回车,程序开始在调试状态下运行:
(gdb) r
Starting program: /../../test2
Enter an integer:5
分析会得到一段输出:
program teminated with signal 11,Segmentation fault.
[New process 9868]
#0 0x004cb1c0 in_IO_vfscanf_internal () form /lib/libc.so.6
(gdb)
从上面可知,错误发生在调用scanf(IO_vfscanf_internal())时,程序是因为段错误而导致崩溃,说明内存操作出了问题。
更加详尽的信息,使用以下语句:
(gdb) bt
得到如下输出:
#0 0x004cb1c0 in_IO_vfscanf_internal () form /lib/libc.so.6
#1
#2 0x0804842b in main() at test2.c:7
(gdb)
前两句是由C函数库来完成, 跳过这两句。由第三局知道程序test2.c的第6行出现错误。找到出错原因,改正,重新编译。
gcc优化代码
优化是现代C编译器的一个最令人心动的特性。优化时编译器的一部分,它可以检查和组合编译器生成的代码,指出未达到最优的部分,重新生成它们,从而使用户编写的程序更加完美且节省空间。gcc拥有强大且是可配置的优化器,从而能够对程序进行优化处理。
使用高级gcc选项
管理大型项目
C为程序员提供一个一种把文件分开的方法,通过使用分离的C模块、函数或被包含在不同的被编译过的.c文件里的数据,把工程分成符合逻辑的不同部分。
假定程序3个模块,分为test2_1.c,test2_2.c,test2_3.c,可使用以下方法 编译整个程序:
gcc -Wall -o program test2_1.c test2_2.c test2_3.c
gcc将编译每个.c文件,并把它们连接起来称为一个可执行文件,若内容稍有改动,需要重新编译全部程序;
将编译分成独立的步骤,先编译每一程序,使用gcc的-c选项,程序生成一个.o文件,该.o文件只包含一个.c文件的内容,不是最终的可执行文件。
gcc -Wall -c -o test2_1.o test2_1.c
gcc -Wall -c -o test2_2.o test2_2.c
gcc -Wall -c -o test2_3.o test2_3.c
最后使用命令将3个.o文件生成一个可执行文件
gcc -o program test2_1.o test2_2.o test2_3.o
改动模块代码,不用重新编译另外两个文件,只要重新编译所改动的文件并且重新连接即可。
gcc -Wall -c -o test2_1.o test2_1.c
gcc -o program test2_1.o test2_2.o test2_3.o
在一个包含几百个甚至更多的文件的工程中,方法更加便利,但操作显得有些复杂。
指定查找路径
当用户建立一个项目时,gcc将使用默认的路径,去查找和使用头文件和库文件,如在编写一个程序时,需要 在查询路径下添加一个目录,使链接器可以找到程序所需的库;编译程序时,需要查找所需的文件下,可使用-I和-L选项。
当编译程序所包含一个文件zw.h,不在默认查询目录下,
gcc -Wall -I/usr/include/zw -o test test.c
预处理就能找到程序所需要的zw.h文件
当连接X11库时,使用相同的方法处理库路径,但注意告诉链接器库的名字,命令:
gcc -L/usr/X11R6/lib -Wall test test.c -1X11
连接库
在程序中连接库,可使用-l选项,该库可为静态,或共享的。该选项只能在编译中连接的最后阶段才能指定(前面要修改Makefile文件,使它在链接的最后阶段包含库),它把所有的.o文件连接起来,若连接数学库
gcc -o test test3a.o test3b.o test3c.o -lm
可直接把.c源文件编译成最终的可执行问价,命令如下:
gcc -Wall -o test test.c -lm
使用管道
管道实现的是使管道前的输出成为管道后的输入。通过使用管道,可同时调用多个程序,一个程序的输出作为输入直接送给另外一个程序,而且还可以一直连续下去,不需要临时文件。
管道编译过程由gcc的-pipe选项决定,使用这个选项,gcc能建立适当的管道,如下
gcc -pipe -Wall -O3 test test.c
当编译大程序时,可节省时间。
gcc编译流程
C预处理器cpp
用来完成宏的求值,条件编译以及其他一些需要在代码传递到编译器前完成的工作。一般"#"号后面的语句由cpp来进行处理,来看下面一段代码
#define FOO (5*2)
...
printf("%d\n",FOO*2);
...
经过cpp预处理后,代码变成下面的形式:
printf("%d\n",(5*2)*2);
cpp:解释宏、处理包含文件、处理#if和#ifdef声明,还有其他以#开头的标志。命令行中使用gcc -E调用cpp,通常gcc会自动调用cpp;
GUN链接器Ld
编写一个较大的程序时,经常把它分成许多独立的模块,需要连接器把所有的模块组合起来,并结合C函数库和初始化代码,产生可执行文件。
通常情况下,ld被编译器gcc所调用,产生可执行代码,但是若想要更好地控制连接过程,最好手工调用ld。
GUN汇编器as
使用gcc编译程序时,产生汇编代码,as会处理这些汇编代码,从而产生目标文件(二进制文件),而目标文件将生成.o文件、库或者最终的可执行文件。as像前两项一样,通常情况下是被gcc调用,但是要使用汇编语言编写程序,可手动调用。
文件处理器ar
可使用ar程序建立静态库,把几个小文件组合成一个大文件。建立静态库时,必须把多个.o文件组合成一个单独的.a文件。
库显示ldd
一个可执行文件可能要使用一些共享库,可通过ldd工具显示它们,ldd是Libaray Dependency Display的缩写。
ldd ./test
其他编译调试工具
C++编译器g++
GUNC++编译器g++和C编译器gcc的格式相同,完成的工作也是一样的。gcc也可编译C++代码,但是需要手动设置一些特殊选项,易出错。
g++所使用选项和gcc一样,为了区别C代码,对C++代码常使用.cxx扩展名;
g++ [-opations] [filename]
EGCS
它集成了FORTRAN等编译器,集成了对gcc的各种改进和pgcc对Pentium的一些优化;
calls
它用来调用gcc 的预处理器处理源程序文件,输出该文件里的函数调用树图。打印调用跟踪结果时,在函数后面用中括号给出函数所在文件的文件名。
main [test.c]
indent
设置indent选项可以指定如何格式化写好的源代码,使源代码产生统一的缩进格式;
gprof
将告诉程序中的每个函数被调用的次数和每个函数执行所占的时间。在编译程序是加上-pg选项,可在程序中使用gprof,它在程序每次执行时产生一个叫gmon.out的文件,gprof就使用这个文件剖析信息。
gprof <filename>
注:gprof产生的剖析数据很大,若想查看该数据,最好把输出重定向到一个文件中。
f2c和p2c
前者将FORTRAN代码转换成C代码,后者把Pascal代码转换成C代码。一般转换小程序可直接使用它们,不许用到命令选项。
g++
多文件编译
asd
-bash-4.2$ ls
client.cpp client.h client_test.cpp
-bash-4.2$ g++ -c client.cpp
源文件
//client.h
//使用编译处理指令 为了避免头文件重复包含,
#ifndef CLIENT_H
#define CLIENT_H
class Client{
private:
static char ServerName;
static int ClientNum;
public:
static void ChangeServerName(char name);
static int getClinetNum();
};
#endif //CLIENT_H
//client.cpp
#include<iostream>
#include "client.h"
using namespace std;
void Client::ChangeServerName(char name){
Client::ServerName = name; //静态数据成员引用,注意加上“类名::”来修饰
Client::ClientNum++;
}
int Clinet::getClinetNum(){
return Client::ClientNum;
}
//client_test.cpp
#include<iostream>
#include "client.h"
using namespace std;
int Client::ClientNum = 0;
char Client::ServerName = 'a';
int main(){
Clinet c1;
c1.ChangeServerName('a');
cout<<c1.getClinetNum()<<endl;
}
编译
-bash-4.2$ g++ -c client.cpp
In file included from client.cpp:1:0:
client.h: In function ‘void ChangeServerName(char)’:
client.h:5:21: error: ‘char Client::ServerName’ is private
static char ServerName;
类的声明在头文件client.h,在client.cpp中添加了头文件之后,编译显示ChangeServerName()函数中使用私有成员?
在client.cpp中添加以下代码,通过编译,?
#include<iostream>
using namespace std;
将源文件.cpp编译成目标文件.o
-bash-4.2$ g++ -c client_test.cpp
编译主函数代码
-bash-4.2$ g++ -c client_test.cpp
将多个目标文件.o链接成可执行文件.out
-bash-4.2$ sudo g++ -o client_test client.o client_test.o -lm
执行可执行文件client_test
-bash-4.2$ ./client_test
1
-bash-4.2$
gcc与g++区别
Makefile