gcc/g++在执行编译工作的时候,总共需要4步
1.预处理,生成.i的文件[预处理器cpp]
2.将预处理后的文件不转换成汇编语言,生成文件.s[编译器egcs]
3.有汇编变为目标代码(机器代码)生成.o的文件[汇编器as]
4.连接目标代码,生成可执行程序[链接器ld]
[参数详解]
-c
只激活预处理,编译,和汇编,也就是他只把程序做成obj文件
例子用法:
gcc -c hello.c
他将生成.o的obj文件
-S
只激活预处理和编译,就是指把文件编译成为汇编代码。
例子用法
gcc -S hello.c
他将生成.s的汇编代码,你可以用文本编辑器察看
-E
只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里
面.
例子用法:
gcc -E hello.c > pianoapan.txt
gcc -E hello.c | more
慢慢看吧,一个hello word 也要与处理成800行的代码
-o
制定目标名称,缺省的时候,gcc 编译出来的文件是a.out,很难听,如果
你和我有同感,改掉它,哈哈
例子用法
gcc -o hello.exe hello.c (哦,windows用习惯了)
-pipe
使用管道代替编译中临时文件,在使用非gnu汇编工具的时候,可能有些问
题
gcc -pipe -o hello.exe hello.c
-ansi
关闭gnu c中与ansi c不兼容的特性,激活ansi c的专有特性(包括禁止一
些asm inline typeof关键字,以及UNIX,vax等预处理宏,
-fno-asm
此选项实现ansi选项的功能的一部分,它禁止将asm,inline和typeof用作
关键字。
-fno-strict-prototype
只对g++起作用,使用这个选项,g++将对不带参数的函数,都认为是没有显式
的对参数的个数和类型说明,而不是没有参数.
而gcc无论是否使用这个参数,都将对没有带参数的函数,认为城没有显式说
明的类型
-fthis-is-varialble
就是向传统c++看齐,可以使用this当一般变量使用.
-fcond-mismatch
允许条件表达式的第二和第三参数类型不匹配,表达式的值将为void类型
-funsigned-char
-fno-signed-char
-fsigned-char
-fno-unsigned-char
这四个参数是对char类型进行设置,决定将char类型设置成unsigned char(前
两个参数)或者 signed char(后两个参数)
-include file
包含某个代码,简单来说,就是便以某个文件,需要另一个文件的时候,就可以
用它设定,功能就相当于在代码中使用#include<filename>
例子用法:
gcc hello.c -include /root/pianopan.h
-imacros file
将file文件的宏,扩展到gcc/g++的输入文件,宏定义本身并不出现在输入文件
中
-Dmacro
相当于C语言中的#define macro
-Dmacro=defn
相当于C语言中的#define macr
三个文件,一个头文件,两个程序文件
d.h
*/
#include <iostream>
using namespace std;
class Dataset
{
public:
int getdata();
};
d.cpp
*/
#include "d.h"
int Dataset::getdata()
{
return 1231;
}
out.cpp
*/
#include <iostream>
#include "d.h"
using namespace std;
int main()
{
Dataset Ds;
cout<<Ds.getdata()<<endl;
return 1;
}
[root@localhost code]# g++ -o test.o d.cpp out.cpp
[root@localhost code]# ./test.o
1231
[root@localhost code]#
编译成动态库
[root@localhost code]# g++ -o d.o -c d.cpp
[root@localhost code]# ar -r d.a d.o
ar: 正在创建 d.a
[root@localhost code]# g++ -o out out.cpp ./d.a
[root@localhost code]# ./out
1231
[root@localhost code]#
-Umacro
相当于C语言中的#undef macro
-undef
取消对任何非标准宏的定义
-Idir
在你是用#include"file"的时候,gcc/g++会先在当前目录查找你所制定的头
文件,如果没有找到,他回到缺省的头文件目录找,如果使用-I制定了目录,他
回先在你所制定的目录查找,然后再按常规的顺序去找.
对于#include<file>,gcc/g++会到-I制定的目录查找,查找不到,然后将到系
统的缺省的头文件目录查找
-I-
就是取消前一个参数的功能,所以一般在-Idir之后使用
-idirafter dir
在-I的目录里面查找失败,讲到这个目录里面查找.
-iprefix prefix
-iwithprefix dir
一般一起使用,当-I的目录查找失败,会到prefix+dir下查找
-nostdinc
使编译器不再系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确
限定头文件的位置
-nostdin C++
规定不在g++指定的标准路经中搜索,但仍在其他路径中搜索,.此选项在创建
libg++库使用
-C
在预处理的时候,不删除注释信息,一般和-E使用,有时候分析程序,用这个很
方便的
-M
生成文件关联的信息。包含目标文件所依赖的所有源代码
你可以用gcc -M hello.c来测试一下,很简单。
-MM
和上面的那个一样,但是它将忽略由#include<file>造成的依赖关系。
-MD
和-M相同,但是输出将导入到.d的文件里面
-MMD
和-MM相同,但是输出将导入到.d的文件里面
-Wa,option
此选项传递option给汇编程序;如果option中间有逗号,就将option分成多个选
项,然后传递给会汇编程序
-Wl.option
此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选
项,然后传递给会连接程序.
-llibrary
制定编译的时候使用的库
例子用法
gcc -lcurses hello.c
使用ncurses库编译程序
-Ldir
制定编译的时候,搜索库的路径。比如你自己的库,可以用它制定目录,不然
编译器将只在标准库的目录找。这个dir就是目录的名称。
-O0
-O1
-O2
-O3
编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最
高
-g
只是编译器,在编译的时候,产生调试信息。
-gstabs
此选项以stabs格式声称调试信息,但是不包括gdb调试信息.
-gstabs+
此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息.
-ggdb
此选项将尽可能的生成gdb的可以使用的调试信息.
-static
此选项将禁止使用动态库,所以,编译出来的东西,一般都很大,也不需要什么
动态连接库,就可以运行.
-share
此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
-traditional
试图让编译器支持传统的C语言特性
g++ 编译和链接
传统意义上的编译程序分两步走 —— 编译和链接:
1.编译(compile):指用编译器(compiler)将源代码(source code)生成二进制目标文件(object file),在Windows下也就是 .obj 文件,UNIX下是 .o 文件。编译时,编译器需要的是语法的正确,函数与变量的声明的正确,编译器只检测程序语法,和函数、变量是否被声明,函数并不需要被定义。
UNIX下g++的语法为:
g++ -c file.cpp
-c 是compile的意思,此命令将会生成 file.o 的目标文件。
2.链接(link):找到所要用到函数所在的目标文件,并把它们链接在一起合成为可执行文件(executable file)。链接时,要确保编译器能找到所有被用到了的函数所在的目标文件。
g++ file1.o file2.o -o program.exe
-o 是指定生成的可执行文件名称(output)。若不给出,默认的名称为 a.out
上述两部通常也可以合在一起完成:
g++ file1.cpp file2.cpp -o program.exe
这完全等同于上面两步的结合,会先生成目标文件,然后链接成 file.exe
3. 库 (library)
对于一个源文件很多的大项目,为了避免重复编译,也为了方便编译器链接,通常会把一些常用 到的目标文件打包(archive),于是就成为了传说中的库文件(library)。在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。
UNIX 所要用到的命令:
1)ar -- create, modify, and extract from archives.
@Usage: ar cr lib****.a file1.o file2.o
**** 为自定义的库文件名。
标签 c (create):如果库不存在,则创建库;
标签 r (replace):如果库中已存在要添加的对象文件,则旧的对象文件将被替换。
实际上 ar 只是一个打包工具,是archive(打包)的首字母。它将一系列的目标文件首位连接在一起,并内嵌一个索引表,使得编译器能够方便地找到所需要的函数。 一般来说,由于函数索引表的存在,对库的链接要比对一般的对象文件的链接更快。如果 ar 未能完成此项索引表工作,还可以手动用以下的 ranlib 命令创建索引表。
2) ranlib -- generate index to archive.
@Usage: ranlib lib****.a
3) nm -- list symbols from object files.
nm可以用来显示 ranlib 所构建的索引表。你将会看到所有库里的函数名(除了模板函数template function)。
4. 在编译时链接库
创建了自己的库,以后要用到相关函数的时候,只需在代码中声明所要用的函数(必须和库中定义得相同)。在链接的时候,需要给出库的名称和位置:
g++ file1.o file2.o -o program.exe -L**** -l****
-L 后紧跟库文件所在的目录地址,-l 后紧跟库名。
编译器在链接的时候会在所指定的目录地址下寻找名为 lib****.a 的库文件。
g++ 编译多个相关文件
三个文件,一个头文件,两个程序文件
d.h
*/
#include <iostream>
using namespace std;
class Dataset
{
public:
int getdata();
};
d.cpp
*/
#include "d.h"
int Dataset::getdata()
{
return 1231;
}
out.cpp
*/
#include <iostream>
#include "d.h"
using namespace std;
int main()
{
Dataset Ds;
cout<<Ds.getdata()<<endl;
return 1;
}
[root@localhost code]# g++ -o test.o d.cpp out.cpp
[root@localhost code]# ./test.o
1231
[root@localhost code]#
编译成静态库
[root@localhost code]# g++ -o d.o -c d.cpp
[root@localhost code]# ar -r d.a d.o
ar: 正在创建 d.a
[root@localhost code]# g++ -o out out.cpp ./d.a
[root@localhost code]# ./out
1231
[root@localhost code]#
GCC 可同时用来编译 C 程序和 C++ 程序。一般来说,C 编译器通过源文件的后缀名来判断是 C 程序还是 C++ 程序。
在 linux 中,C 源文件的后缀名为 .c,而 C++ 源文件的后缀名为 .C 或 .cpp。
但是,gcc 命令只能编译 C++ 源文件,而不能自动和 C++ 程序使用的库连接。
因此,通常使用 g++ 命令来完成 C++ 程序的编译和连接,该程序会自动调用 gcc 实现编译。
假设我们有一个如下的 C++ 源文件(main.cpp):
#include <iostream>
using namespace std;
int main()
{
cout << "Hello, world!" << endl;
return 0;
}
则可以如下调用 g++ 命令编译、连接并生成可执行文件:
[root@zeckey hello_world]# g++ -o out main.cpp
[root@zeckey hello_world]# ./out
Hello, world!
[root@zeckey hello_world]#
注解:
1. -o out 是生成指定的输出文件名为out,当然你也可以改用其他你喜欢的名字。用在生成可执行文件时。
2. 如果你喜欢用 gedit 这个编辑编写源程序,可能会出现如下错误:
[root@zeckey hello_world]# g++ main.cpp -o main.o
main.cpp: In function `int main()':
main.cpp:9: stray '\343' in program
main.cpp:9: stray '\200' in program
main.cpp:9: stray '\200' in program
[root@zeckey hello_world]#
这时你可以用 vi 编辑器查看源程序 main.cpp ,可能是如下所示:
#include <iostream>
using namespace std;
int main()
{
cout << "Hello, world!" << endl;
return 0;
}
这柯能是 gedit 编辑器自身带进来的解码错误,所以你最好还是用 vi 这个最经典的编辑器编写程序代码,
可以用 gedit 查看程序代码,如果你确实不习惯的话。
gcc/g++编译器使用简介
GNU CC(简称gcc)是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++、Object C、Jave等多种语言编写的程序。gcc又可以作为交叉编译工具,它能够在当前CPU平台上为多种不同体系结构的硬件平台开发软件,非常适合在嵌入式领域的开发编译,如常用的arm-linux-gcc交叉编译工具
通常后跟一些选项和文件名来使用 GCC编译器。gcc命令的基本用法如下:
gcc [options] [filenames]
选项指定编译器怎样进行编译。
一、gcc编译流程
1.预处理-Pre-Processing
gcc -E test.c -o test.i //.i文件
2.编译-Compiling
gcc -S test.i -o test.s //.s文件
3.汇编-Assembling //.o文件
gcc -c test.s -o test.o
4.链接-Linking //bin文件
gcc test.o -o test
二、gcc工程惯用
1.编译
gcc -c test.c //.o文件,汇编
gcc -o test test.c //bin可执行文件
gcc test.c //a.out可执行文件
如果是c++直接将gcc改为g++即可。
2.常用参数
1)-E参数
-E选项指示编译器仅对输入文件进行预处理。当这个选项被使用时,预处理器的输出被送到标准输出而不是储
存在文件里.
2)-S参数
-S编译选项告诉 GCC在为 C代码产生了汇编语言文件后停止编译。 GCC产生的汇编语言文件的缺省扩展名
是 .s。
3)-c参数
-c选项告诉 GCC仅把源代码编译为目标代码。缺省时 GCC建立的目标代码文件有一个 .o的扩展名。
4)-o参数
-o 编译选项来为将产生的可执行文件用指定的文件名。
5)-O参数
-O选项告诉 GCC对源代码进行基本优化。这些优化在大多数情况下都会使程序执行的更快。 -O2选项告诉
GCC产生尽可能小和尽可能快的代码。如-O2,-O3,-On(n常为0--3);
-O 主要进行跳转和延迟退栈两种优化;
-O2除了完成-O1的优化之外,还进行一些额外的调整工作,如指令调整等。
-O3则包括循环展开和其他一些与处理特性相关的优化工作。
选项将使编译的速度比使用 -O时慢,但通常产生的代码执行速度会更快。
如:
[root@localhost test]# gcc test.c -O3
[root@localhost test]# gcc -O3 test.c
[root@localhost test]# gcc -o tt test.c -O2
[root@localhost test]# gcc -O2 -o tt test.c
6)调试选项-g和-pg
GCC支持数种调试和剖析选项,常用到的是 -g和 -pg。
-g选项告诉 GCC产生能被 GNU调试器使用的调试信息以便调试你的程序。GCC提供了一个很多其他 C编译
器里没有的特性,在 GCC里你能使-g和 -O (产生优化代码)联用。
-pg选项告诉 GCC在编译好的程序里加入额外的代码。运行程序时,产生 gprof用的剖析信息以显示你的程序的
耗时情况。
7) -l参数和-L参数
-l参数就是用来指定程序要链接的库,-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?
就拿数学库来说,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的头lib和尾.so去掉就是库名
了。
如:
gcc xxx.c -lm( 动态数学库) -lpthread
8) -include和-I参数
-include用来包含头文件,但一般情况下包含头文件都在源码里用#i nclude xxxxxx实现,-include参数很少用。-I参
数是用来指定头文件目录,/usr/include目录一般是不用指定的,gcc知道去那里找,但是如果头文件不
在/usr/icnclude里我们就要用-I参数指定了,比如头文件放在/myinclude目录里,那编译命令行就要加上-I/myinclude
参数了,如果不加你会得到一个"xxxx.h: No such file or directory"的错误。-I参数可以用相对路径,比如头文件在当前
目录,可以用-I.来指定。上面我们提到的--cflags参数就是用来生成-I参数的。
9)-Wall、-w和 -v参数
-Wall 打印出gcc提供的警告信息
-w 关闭所有警告信息
-v 列出所有编译步骤
四.几个相关的环境变量
PKG_CONFIG_PATH:用来指定pkg-config用到的pc文件的路径,默认是/usr/lib/pkgconfig,pc文件是文本文件,扩展名是.pc,里面定义开发包的安装路径,Libs参数和Cflags参数等等。
CC:用来指定c编译器。
CXX:用来指定cxx编译器。
LIBS:跟上面的--libs作用差不多。
CFLAGS:跟上面的--cflags作用差不多。
CC,CXX,LIBS,CFLAGS手动编译时一般用不上,在做configure时有时用到,一般情况下不用管。
环境变量设定方法:export ENV_NAME=xxxxxxxxxxxxxxxxx
五.关于交叉编译
交叉编译通俗地讲就是在一种平台上编译出能运行在体系结构不同的另一种平台上,比如在我们地PC平台(X86 CPU)上编译出能运行在arm CPU平台上的程序,编译得到的程序在X86 CPU平台上是不能运行的,必须放到arm CPU 平台上才能运行。当然两个平台用的都是linux。这种方法在异平台移植和嵌入式开发时用得非常普遍。相对与交叉编译,我们平常做的编译就叫本地编译,也就是在当前平台编译,编译得到的程序也是在本地执行。用来编译这种程序的编译器就叫交叉编译器,相对来说,用来做本地编译的就叫本地编译器,一般用的都是gcc,但这种gcc跟本地的gcc编译器是不一样的,需要在编译gcc时用特定的configure参数才能得到支持交叉编译的gcc。为了不跟本地编译器混淆,交叉编译器的名字一般都有前缀,比如armc-xxxx-linux-gnu-gcc,arm-xxxx-linux-gnu- g++ 等等
交叉编译器的使用方法
使用方法跟本地的gcc差不多,但有一点特殊的是:必须用-L和-I参数指定编译器用arm系统的库和头文件,不能用本地(X86)的库(头文件有时可以用本地的)。
例子:
arm-xxxx-linux-gnu-gcc test.c -L/path/to/sparcLib -I/path/to/armInclude