Linux环境GCC介绍

一、GCC概述

GCC(英文全拼:GNU Compiler Collection)是 GNU 工具链的主要组成部分,是一套以 GPL 和 LGPL 许可证发布的程序语言编译器自由软件,由 Richard Stallman 于 1985 年开始开发。
GCC 原名为 GNU C语言编译器(GUN C Compiler),因为它原本只能处理 C 语言,但如今的 GCC ,不仅可以编译 C、C++ 和 Objective-C,还可以通过不同的前端模块支持各种语言,包括 Java、Fortran、Ada、Pascal、Go 和 D 语言等等。
GCC支持多种硬件开发平台,还能进行跨平台交叉编译。此外,GCC是按模块化设计的,可以加入新语言和新CPU架构的支持。

GCC、gcc、g++三者之间的关系

gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。

gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。

1、gcc基本命令

gcc的基本语法是:

gcc [options] [filenames]

其中[options]表示参数,[filenames]表示相关文件的名称。
一些常用的参数及含义下表所示:

参数名称   含义
-E仅执行预处理,不进行编译、汇编和链接(生成后缀为 .i 的预编译文件)
-S执行编译后停止,不进行汇编和链接(生成后缀为 .s 的预编译文件)
-c    编译程序,但不链接成为可执行文件(生成后缀为 .o 的文件)
-o直接生成可执行文件
-O/-O1/-O2/-O3优化代码,减少代码体积,提高代码效率,但是相应的会增加编译的时间
-Os    优化代码体积(多个-O参数默认最后一个)
-Og    代码优化(不能与“-O”一起用)
-O0    关闭优化
-l [lib](这里是小写的L,命令无中括号,下同)指定程序要链接的库,[lib]为库文件名称。如果gcc编译选项中加入了“-static”表示寻找静态库文件
-L [dir]指定-l(小写-L)所使用到的库文件所在路径
-I [dir](这里是大写的I)增加 include 头文件路径
-D [define]预定义宏
-static链接静态库生成目标文件,禁止使用动态库(在支持动态链接的系统上)
-share尽量使用动态库,但前提是系统存在动态库,生成的目标文件较小
-shared生成共享文件,然后可以与其它文件链接生成可执行文件
-fpic生成适用于共享库的与地址无关的代码(PIC)(如果机器支持的话)
-fPIC生成与位置无关的的代码,适用于使用动态库,与“-fpic”的区别在于去除去全局偏移表的任何限制(如果机器支持的话)
-fPIE使用与地址无关的代码生成可执行文件
-w不输出任何警告信息
-Wall开启编译器的所有警告选项
-g生成调试信息,方便gdb调试
-v查看gcc编译器的版本,显示gcc执行时的详细过程
-ggdb加入GDB调试器能识别的格式
-Werror将所有的警告当成错误进行处理,在所有产生警告的地方停止编译
-M生成适合于make规则的主要文件的依赖信息
-MM与“-M”相比忽略由“#include”所造成的依赖
-MD与-M作用类似,将输出导入到 .d 文件中
-MMD与-MM作用类似,将输出导入到 .d 文件中
–help查看帮助信息(注意前面是两个“-”,一个“-”不行)
–version查看版本信息(注意前面是两个“-”,一个“-”不行)

2、gcc编译C语言过程示例

首先创建一个hello.c文件作为实例

#include<stdio.h>
int main(void)
{
    printf("Hello World!");
    return 0;
}

要编译这个程序,只要在命令行下执行如下命令:

gcc -Wall hello.c -o hello
./hello

gcc 编译器会生成一个名为hello的可执行文件,然后执行./hello就可以看到程序的输出结果了。 

显示编译过程详细信息:

gcc -v -Wall hello.c -o hello
./hello

3、gcc编译过程

在终端输入以下命令:

gcc -Wall -save-temps hello.c

 可以看出gcc编译过程生成了一系列中间文件。

gcc编译过程分成预处理(Pre-Processing)、编译(Compiling)、汇编(Assembling)、链接(Linking) 四个步骤。

Step1:预处理

预处理通过对宏定义(像#define)进行展开,对头文件(像 stdio.h)进行展开,对条件进行(像ifdef)编译,展开所有宏,删除所有注释。预处理cpp把源代码以及头文件预编成一个.i文件。命令如下:

gcc -E hello.c -o hello.i

gcc的-E参数,可以让编译器在预编译后停止,并输出预编译结果(hello.i文件)。

Step2:编译

编译也就是检查语法是否错误,将预处理过的文件编译成汇编(.s)文件。命令如下:

gcc -S hello.i -o hello.s

Step3:汇编

汇编也就是将汇编(.s)文件生成目标文件(二进制文件)。通过汇编,文本代码变成了二进制代码(二进制代码文件以.o为后缀名)。命令如下:

gcc -c hello.s -o hello.o

将源文件(.c)文件生成目标文件(二进制文件)。系统自动生成对应的二进制代码(二进制代码文件以.o为后缀名)。

gcc -c hello.c

Step4:链接

链接过程就是找到依赖的库文件(静态与动态),将目标文件链接为可执行程序。命令如下:

gcc [目标文件] -o [可执行程序] -l[动态库名]

假如没有动态库的话,直接使用以下命令:

gcc [目标文件] -o [可执行程序]

对于本例,则输入命令如下:

gcc hello.o -o hello

注意:链接顺序从左向右查找。

4、链接库

刚才实现的hello world输出,printf这个函数本身并没有实现,只是在用这个函数,查看头文件/usr/include/stdio.h,可以看到它的声明。

可是只有声明,没有实现,代码并不能正常执行。实际上,在C语言中,这些实现的源代码并不会直接给你呈现,而是以动静态库的形式存储的。

所以一般链接的时候呢,是从头文件中找函数的声明,从库中找文件的实现。把自己的代码和库中的代码以某种方式关联起来,形成可执行程序。

以下是在不同环境下,动静态库所对应的后缀:

.a是静态库,多个.o练链接得到,用于静态链接,相当于win上.lib;

.so是共享库,用于动态链接,相当于win上.dll;

那么什么是动静态链接呢?

(1)动态链接

写好C语言程序之后,有一些库函数比如printf,scanf,strlen等等,这些在被编译之时,编译器会将其替换成库中的这个函数的地址。

这样在执行到这个函数的时候,编译器便可以根据这个地址找到这个库乃至找到这个函数。

(2)静态链接

这个与动态链接不同的是:这些程序在编译时,编译器会直接将库中方法的实现,整体拷贝一份到我们的可执行程序中!而不是那个函数的地址了。

但是缺点也很明显:会占用资源,想想好几份相同的代码拷贝到这里,再次运行一定占用大量的资源.当然优点是不会再依赖库。动态链接和它相反。

注意:使用动态链接也必须要有.so动态库文件。 使用静态链接也必须使用.a静态库文件。

指定要链接的库,使用示例如下:

 gcc hello.c  /usr/lib/libm.a -o hello
 gcc hello.c -lm -o hello

 两句指令是相等的。标准库名libxxx.a等价于-lxxx(程序在运行时,会在/usr/lib和/lib等目录中查找需要的库文件,-lxxx只会在Linux标准库路径查找)

(3)使用链接器链接库

1.Linux标准库路径:/lib:/usr/lib

在默认情况下,C链接程序只会在Linux标准库路径下搜索标准C语言库。所以,对于开发者而言,仅仅将非标准C语言库文件放在标准目录中,就希望编译器能够找到它是不够的,库文件必须遵循特定的命名规范并且需要在命令行中明确指定。

2.库文件命名规范

lib + 库名 + .a/.so

3.命令行指定要链接的库,使用示例如下:

(1)指定链接Linux标准库路径下的非标准C语言库(这种情况下,库路径已知,可省略)

gcc  -o  fred  fred.c  /usr/lib/libm.a //指定链接静态库

gcc  -o  fred  fred.c  -lm //优先链接动态库

(2)指定链接非Linux标准库路径下的非标准C语言库

gcc  -o  fred  fred.c  /usr/openwin/lib/libX11.so //指定链接动态库

gcc  -o  fred  -L/usr/openwin/lib  -lX11 fred.c //有限链接动态库

总结:对于Linux标准库路径下的库,我们只需要用-l或者库路径来指明要链接的库;对于非Linux标准库路径下的库,我们需要用-L指明库所在目录,并用-l或者库路径来指明要链接的库。

对于用-l或者库路径的区别:-l让链接器优先选择链接同名动态库,库路径使得链接器直接链接库路径指定的库。

4.构建自己的静态库

目标:将file1.c file2.c打包成一个.a库

工具:ar

步骤:

1、gcc  -c  file1.c  file2.c //编译不链接,创建file1.o  file2.o

2、ar  -crv  libfoo.a  file1.o  file2.o

(ar cr  libfoo.a  file1.o  file2.o)

 查看库里面的目标文件:

ar t libfoo.a
5.构建自己的动态库

目标:将file1.c和file2.c打包成一个.so库

工具:ar

步骤:

1、gcc  -c  file1.c  file2.c //编译不链接,创建file1.o  file2.o

2、gcc  -shared  -o  libfoo.so  file1.o  file2.o

6.编译链接并运行链接到动态链接库的程序

1.gcc -o program program.c -L. -lfoo

2.将libfoo.so路径添加到LD_LIBRARY_PATH环境变量,使得program运行时能正确加载到

3. ./program

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值