5. gcc编译器
GNU CC (简称为 gcc)是GNU项目中符 合ANSIC标准的编译系统,能够编译用 C、C++和ObjectC等语言编写的程序。 gcc不仅功能强大,而且可以编译如 C、 C++、Object C、Java、 Fortran、 Pascal Modula-3和 Ada等多种语言,而且gcc又是一个交叉平台 编译器,它能够在当前CPU平台上为多种不同体系结构的硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译。
5.1 安装(c语言中文网)
由于 Linux 操作系统的自由、开源,在其基础上衍生出了很多不同的 Linux 操作系统,如 CentOS、Ubuntu、Debian 等。这些 Linux 发行版中,大多数都默认装有 GCC 编译器(版本通常都较低)。
如果不清楚当前使用的 Linux 发行版是否已经装有 GCC 编译器,或者忘记了已安装 GCC 的版本号,可以打开命令行窗口(Terminal)并执行如下指令:
[root@VM-24-17-centos linux5]# gcc --version gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-4) Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
如果没有安装:
bash: /usr/bin/gcc: No such file or directory
5.1.1 快速安装
要知道,每个 Linux 发行版都有自己的软件包管理工具,比如 CentOS 系统的 yum 包管理器、Ubuntu 系统的 apt 包管理器等等,并且大多数 Linux 发行版都提供有 GCC 编译器的二进制软件包。因此,我们可以直接“傻瓜式”地安装 GCC 编译器(以 yum 为例):
yum -y install gcc yum -y install gcc-c++
注意:切勿认为 gcc 只能用来编译 C 语言程序,g++ 只能用于编译 C++ 程序,这是不对的。
需要注意的是,采用此方式安装的 GCC 编译器,版本通常较低。以我当前使用的 Centos 6.5 系统为例,通过执行以上 2 条指令,其安装的是 GCC 版本为 4.4.7,而当下 GCC 编译器已经迭代至 10.0.1 版本。
这意味着,如果读者使用此方式安装 GCC 编译器,需要查看 GCC 编译器的版本(通过gcc --version指令)是否符合自己的需求。举个例子,如果读者想编译 C++11 标准下的 C++ 程序,则至少要安装 4.8 版本的 GCC 编译器,低版本的 GCC 编译器是不支持 C++11 标准的。
总的来说,如果读者对 GCC 编译器的版本没有要求,则推荐使用此安装方式;反之,如果读者需要安装指定版本的 GCC 编译器,则需要使用接下来介绍的安装方法。
5.1.2 手动安装
此方式需要耗费的时间较长(几个小时),但支持安装指定版本的 GCC 编译器,并适用于大多数 Linux 发行版(不同之处会有额外提示);同时,如果读者想对已安装的 GCC 编译器进行版本升级,也可以使用此方式。
和使用 yum 自动安装 GCC 编译器不同,手动安装 GCC 编译器需要提前到 GCC 官网下载指定版本的 GCC 源码安装包,读者可直接点击GCC源码包进行下载。值得一提的是,每个版本中都包含 2 种格式的压缩包,分别为 tar.gz 和 tar.xz,只是压缩格式不同,本节以 tar.gz 压缩包教大家安装 GCC 编译器。
这里以在 CentOS 系统上安装 10.1.0 最新版本的 GCC 编译器为例,下载的是 gcc-10.1.0.tar.gz 源码压缩包,整个安装过程如下:
1.以源码的方式安装 GCC 编译器,即手动编译 GCC 编译器的源码,需要当前系统中存在一个可用的编译器,我们可以用旧版本的 GCC 编译器来编译安装新版本的 GCC 编译器。
如果读者所用的操作系统已安装有旧版本的 GCC 编译器,则无需另行安装;反之,读者需要先运行如下命令,安装一个旧版本的 GCC 编译器:
yum install -y glibc-static libstdc++-static yum install -y gcc gcc-c++
再次强调,不同 Linux 发行版的软件管理器也有所不同,比如 yum 仅适用于 CentOS、RedHat、Fedora 发行版;而 Ubuntu 系统需使用 apt 完成安装。
其中,第一行指令用于安装编译 C 和 C++ 代码所需的静态链接库;第二行指令用于安装编译 C 和 C++ 代码的 gcc 和 g++ 指令。
2.找到下载好的 gcc-10.1.0.tar.gz 安装包,将其解压至 /usr/local/ 目录下,解压命令为:
[root@bogon local]# tar -xf gcc-10.1.0.tar.gz -C /usr/local/
由此,在 usr/local/ 目录下,就生成了一个新的名为 gcc-10.1.0 的目录(也就是文件夹)。
3.紧接着执行如下指令,下载安装 GCC 所需要的依赖包(如 gmp、mpfr、mpc 等):
[root@bogon local]# cd /usr/local/gcc-10.1.0 [root@bogon gcc-10.1.0]# ./contrib/download_prerequisites
注意,一定观察此命令的执行结果,保证其确实是将 gmp、mpfr、mpc 等依赖包成功下载下来,才能继续执行下面的安装步骤。
4.完成以上准备工作之后,就正式进入安装 GCC 编译器的环节。首先,我们需要手动创建一个目录,用于存放编译 GCC 源码包生成的文件。执行如下命令:
[root@bogon local]# mkdir gcc-build-10.1.0 [root@bogon local]# cd gcc-build-10.1.0
由此,我们在 /usr/local 目录下手动创建了名为 gcc-build-10.1.0 的目录文件,并进入到该目录中。
5.同时,由于 GCC 编译器支持多种编程语言的编译,而实际情况中我们可能只需要编译 1~2 种编程语言,因此需要对其进行必要的配置。通过执行如下指令,可以配置 GCC 支持编译 C 和 C++ 语言:
[root@bogon gcc-build-10.1.0]# ../gcc-10.1.0/configure --enable-checking=release --enable-languages=c,c++ --disable-multilib
有关 configure 后跟的各个参数的含义,读者仅需要了解 --enable-languages 用于设定 GCC 编译器支持编译的编程语言的类别,例如 c、c++、java、objc、obj-c++、go 等。
6.在第 4 步创建好 makefile 文件之后,接下来就可以使用 make 命令来编译 GCC 源程序:
[root@bogon gcc-build-10.1.0]# make
注意,编译过程是非常耗时的(本机耗时 6 小时完成编译),因此如果读者选用此方式安装 GCC,则在执行 make 命令时一定要安排合适的时间。
7.最后,执行如下命令安装 gcc:
[root@bogon gcc-build-10.1.0]# make install
由此就成功安装了 10.1.0 版本的 GCC 编译器。需要注意的是,如果此时读者直接执行 gcc --version,则 gcc 版本仍会显示之前安装的版本。操作系统重启之后,GCC 版本就会自行更正过来。
5.2 gcc 编译过程
#include <stdio.h> int main(){ puts("hello,world!"); return 0; }
gcc编译过程分为4个步骤:
- 预处理( Pre-Processing )
- 编译 ( Compiling )
- 汇编 (Assembling )
- 链接 ( Linking )
5.2.1 预处理阶段
-E:预处理的主要作用是通过预处理的内建功能对一些可预处理资源进行等价替换,最常见的可预处理资源有:文件包含、条件编译、布局控制、宏处理等。
gcc的选项,-E可以使编译器在预处理结束时就停止编译,生成.i文件(作用:把头文件嵌入)
gcc -E -o [目标文件] [编译文件] # 例如 gcc -E -o hello.i hello.c
5.2.2 编译阶段
gcc的选项,-S,生成.s文件(作用:检查代码的规范性、是否有语法错误等,以确定代码实际要做的工作,在检查无误后,就开始把代码翻译成汇编语言。)
.s是汇编语言原始程序
gcc -S -o hello.s hello.c
hello.s
可以直接执行。
5.2.3 汇编阶段
gcc选项,-c
,汇编阶段是把编译阶段生成的.S
文件 转换成 二进制目标代码(.o
目标文件)
gcc -c hello.s -o hello.o
5.2.4 链接阶段
完成了链接之后,gcc就可以生成可执行文件,其命令格式如下。
gcc hello.o -o hello
运行该可执行文件即可:
./hello
5.3 gcc所支持的后缀名
5.4 gcc 常用编译选项
1.常用选项:
2.库相关选项:
默认情况下库文件的存放位置:/usr/lib
或/lib
。
3.警告和出错选项:
4.优化选项:
gcc可以对代码进行优化,它通过编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的整数。
对于不同版本的gcc来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。
通常情况下,数字越大,会起到更好的优化效果,但整个编译链接的过程会变慢。
5.5 库文件的创建
1.库文件的分类:
静态库文件:
指编译链接时,把库文件的代码全部加入到可执行文件中,生成的可执行文件变大,运行时不再需要库文件,后缀一般a。
动态库文件:
编译链接时,没有把库文件的代码加入到可执行文件中,在执行的时候去访问库文件,节省系统开销,生成的文件也小,后缀一般.so。
2.静态库文件的创建(见例2):
1.编写源代码:xxx.c
2.编译成一个目标文件:xxx.o
3.执行命令,产生静态库文件:
ar -cr libxxx.a xxx.o
3.动态库文件的创建(见例3):
1.编写源代码:xxx.c
2.执行命令,产生动态库文件:
gcc -shared -fpic -o libxxx.so xxx.c # -shared: 生成共享库 # -fpic: 产生位置无关代码
【例子1】:
头文件方式调用
./test.c
#include <stdio.h> //这个是在/usr/lib中找的 #include "mytest.h" //这个是在当前目录找的 int main(){ mytest(); printf("hello!\n"); return 0; }
./mytest.h(头文件)
#include <stdio.h> void mytest(){ printf("mytest() 头文件方式调用!\n"); }
【例2】
静态库文件方式调用:
./test.c
#include <stdio.h> int main(){ Mytest(); printf("hello!\n"); return 0; }
./mytest.c
#include <stdio.h> void mytest(){ printf("mytest() 静态库方式调用!\n"); }
执行:
# 编译 gcc -c mytest.c -o mytest.o # 生成静态库文件 ar -cr -o libmytest.a mytest.o
编译:
gcc test.c -o test -L ./ -l libmytest.a # 错误 # 指明库文件名称时,要使用简写形式,例如: # -l libmytest.a 简写为:-lmytest # 即: [root@VM-24-17-centos linux5]# gcc test.c -o test -L ./ -lmytest test.c: In function ‘main’: test.c:3:5: warning: implicit declaration of function ‘mytest’ [-Wimplicit-function-declaration] mytest(); ^~~~~~ [root@VM-24-17-centos linux5]# ./test mytest() 静态库方式调用! hello!
【例3】
动态库文件方式调用:
# 生成动态库文件 gcc -shared -fpic -o libmytest.so mytest.c # 编译 gcc test.c -o test -L ./ -lmytest ./test # 此时执行./test,会报错: ./test: error while loading shared libraries: libmytest.so: cannot open shared object file: No such file or directory # 因为默认库文件在/usr/lib或者/lib中,而动态库文件是在运行时访问,在执行./test的时候,默认库文件放置位置是找不到刚刚生成的动态库的,所以运行前必须把动态库文件复制到/usr/lib目录 cp libmytest.so /usr/local/lib ./test # 如果还报错: vim /etc/ld.so.conf add/usr/lib sudo ldconfig # 刷新即可