Linux GCC 编译详解

一、GCC 编译器简介

首先,什么是编译器呢?
我们可以使用编辑器(如 linux 下的 vi、windows 下的记事本)来写程序(编辑代码)。但是我们写的程序中的代码语句,电脑是看不懂的,我们需要把它转成电脑能懂的语句,编译器就是这样的转化工具。就是说,我们用编辑器编写程序,由编译器编译后才可以运行!编译器是将易于编写、阅读和维护的高级计算机语言翻译为计算机能解读、运行的低级机器语言的程序

那什么是 GCC 编译器?

  1. gcc(GNU Compiler Collection,GNU 编译器套件),是由 GNU 开发的编程语言编译器。gcc 作为GNU操作系统的官方编译器,现已被大多数类 Unix 操作系统(如Linux、BSD、Mac OS X等)采纳为标准的编译器,gcc 同样适用于微软的 Windows。
  2. gcc 最初用于编译 C 语言,随着项目的发展 gcc 已经成为了能够编译 C、C++、Java、Ada、fortran、Object C、Object C++、Go 语言的编译器大家族。
  3. GCC 常用来编译 C/C++ 语言:
    1. 它不仅支持 C 的许多“方言”,也可以区别不同的 C/C++ 语言标准;
    2. 也可以使用命令行选项来控制编译器在翻译源代码时应该遵循哪个 C/C++ 标准。例如,当使用命令行参数 -std=c99 启动 GCC 时,则编译器支持 C99 标准。

如何安装使用 GCC 呢?
linux ubuntu 下,可以通过 sudo apt install gcc g++ 安装命令安装
查看版本 gcc/g++ -v/–version:

yxm@192:~$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
Copyright (C) 2015 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.

【注意】如果需要支持 c++11,gcc 版本需要 > 4.8.5。

二、GCC 工作流

编程语言的发展

计算机语言的发展 总体分三个阶段

  • 第一代机器语言:机器语言是微处理器理解和使用的,用于控制它的操作二进制代码。
    • 优点:直接执行,速度快,资源占用少
    • 缺点:可读性、可移植性差,编程繁杂
  • 第二代汇编语言 :一种面向机器的低级语言,通常是为特定的计算机或系列计算机专门设计的。汇编语言是机器指令的符号化表示,故不同的机器就有不同的汇编语言。使用汇编语言能面向机器并较好地发挥机器的特性,得到质量较高的程序。汇编语言虽然能编写高效率的程序,但是学习和使用都不是易事,并且很难调试。
  • 第三代高级语言:高级语言主要是相对于汇编语言而言的,它是较接近自然语言和数学公式的编程,基本脱离了机器的硬件系统,用人们更易理解的方式编写程序。编写的程序称之为源程序

GCC 工作流程

从上面的编程语言的发展过程可以知道,我们现在常说的编程语言一般都是高级语言,编写的程序称之为源程序。编译器其实就相当于一个翻译器,将高级语言翻译成机器语言以便机器可以识别。

gcc 编译器从拿到一个c 源文件到生成一个可执行程序,中间一共经历了四个步骤:
四个步骤并不是 gcc 独立完成的,而是在内部调用了其他工具,从而完成了整个工作流程:
【注意】头文件展开:其实就是把头文件中的内容复制到 .c/.c++ 源代码文件中。

gcc 和 g++ 的区别

理解了 GCC 的工作流程,接下来说一下 gcc 和 g++ 的区别,相信你会有更加深刻的了解。

首先,gcc 和 g++都是GNU(组织)的一个编译器。

误区一:gcc 只能编译 c 代码,g++ 只能编译 c++ 代码。其实两者都可以编译,请注意:

  • 后缀为 .c 的,gcc 把它当作是 C 程序,而 g++ 当作是 c++ 程序;
  • 后缀为 .cpp 的,两者都会认为是 C++ 程序,C++ 的语法规则更加严谨一些。
  • 编译阶段,g++ 会调用 gcc,对于 C++ 代码,两者是等价的,但是因为 gcc 命令不能自动和 C++ 程序使用的库联接,所以通常用 g++ 来完成链接,为了统一起见,干脆编译/链接统统用 g++ 了,这就给人一种错觉,好像 cpp 程序只能用 g++ 似的。

误区二:gcc 不会定义 __cplusplus 宏,而 g++ 会

  • 实际上,这个宏只是标志着编译器将会把代码按 C 还是 C++ 语法来解释
  • 如上所述,如果后缀为 .c,并且采用 gcc 编译器,则该宏就是未定义的,否则,就是已定义

误区三:编译只能用 gcc,链接只能用 g++

  • 严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用 gcc/g++,而链接可以用 g++ 或者 gcc -lstdc++。
  • gcc 命令不能自动和C++程序使用的库联接,所以通常使用 g++ 来完成联接。但在编译阶段,g++ 会自动调用 gcc,二者等价

总结:看到上面的误区,你可能能够理解了,但是使用起来比较晕,那么不妨给自己一个规则:gcc 编译 c 代码,g++ 编译 c++ 代码

三、使用 GCC 编译

GCC 编译格式

首先,了解一下,GCC 编译命令的格式:
命令 选项 输出可执行文件的文件名 文件名
gcc [options] [file] file…
g++ [options] [file] file…

  • 命令、选项和源文件之间使用空格分隔;
  • 一行命令中可以有零个、一个或多个选项;
  • 文件名可以包含文件的绝对路径,也可以使用相对路径;
  • 如果命令中不包含输出可执行文件的文件名,可执行文件的文件名会自动生成一个默认名,Linux平台为a.out,Windows平台为 a.exe

GCC 常用选项

选项作用
-o指定生成的输出文件名为file
【注意】除了上面的编译格式,-o 还可以使用另外一种编译格式,如下所示:
命令 文件名 选项 输出可执行文件的文件名
gcc file… [options] [file]
g++ file… [options] [file]
-E只进行预处理
-S(大写)只进行预处理和编译
-c(小写)只进行预处理、编译和汇编
-v / --version查看gcc版本号
-g包含调试信息,该程序可以被调试器调试
-On n=0~3编译优化,n越大优化得越多。n的取值范围:0~3。编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
-Wall提示更多警告信息
-D编译时定义宏
-L指定编译的时候,搜索的库的路径。
-I directory即指定 include 包含文件的搜索目录
-w不生成任何警告信息
-l在程序编译的时候,指定使用的库
-std指定C方言,如:-std=c99,gcc默认的方言是GNU C
-shared生成共享目标文件,通常用在建立共享库时

【注意】上表中仅列出了常用的一些指令选项,事实上这仅是冰山一角,GCC 编译器提供有大量的指令选项,可满足我们在大部分场景下的编译需求。有关更多的编译指令,感兴趣的读者可自行查看 GCC 手册

GCC 编译流程

如上面的文章所讲,C 或者 C++ 程序从源代码生成可执行程序的过程,需经历 4 个过程,分别是预处理、编译、汇编和链接。同样,使用 GCC 编译器编译 C 或者 C++ 程序,也必须要经历这 4 个过程。下面以运行 C 语言程序为例,给大家演示如何使用 gcc 快速获得对应的可执行程序。如下就是一段 C 语言程序:

// test.c 文件
#include <stdio.h>
#define PI 3.14
int main() 
{
    int sum = PI + 10;
    printf("Hello World\n");
    return 0;
}
# 第一步: 进行预处理
yxm@192:~$ gcc -E test.c  -o test.i
yxm@192:~$ ls
company_project  install  myshare  test.c  test.i

# 第二步: 生成汇编文件
yxm@192:~$ gcc -S test.i  -o test.s
yxm@192:~$ ls
company_project  install  myshare  test.c  test.i  test.s

# 第三步: 生成目标代码
yxm@192:~$ gcc -c test.s  -o test.o
yxm@192:~$ ls
company_project  install  myshare  test.c  test.i  test.o  test.s

# 第四步: 生成可以执行文件
yxm@192:~$ gcc test.o  -o test
yxm@192:~$ ls
company_project  install  myshare  test  test.c  test.i  test.o  test.s

# 第五步: 执行 
yxm@192:~$ ./test
Hello World

但考虑在实际使用中,我们可能并不关心程序的执行结果,只想快速得到最终的可执行程序,因此 gcc 和 g++ 都对此需求做了支持,可以直接将源文件生成一个可以执行文件

yxm@192:~$ gcc test.c  -o test
yxm@192:~$ ls
company_project  install  myshare  test  test.c
yxm@192:~$ ./test
Hello World

可以看到,GCC 编译器支持使用 gcc(g++)指令“一步编译”指定的 C(C++)程序。【注意】虽然我们仅编写了一条 gcc 或者 g++ 指令,但其底层依据是按照预处理、编译、汇编、链接的过程将 C 、C++ 程序转变为可执行程序的。而本应在预处理阶段、编译阶段、汇编阶段生成的中间文件,此执行方式默认是不会生成的,只会生成最终的 a.out 可执行文件。

如果不指定输出文件名字,gcc 编译器会生成一个默认的可以执行文件 a.out

yxm@192:~$ gcc test.c 
yxm@192:~$ ls
a.out  company_project  install  myshare  test.c
yxm@192:~$ ./a.out 
Hello World

gcc -D 选项:下面以运行 C 语言程序为例,给大家演示如何使用 gcc -D 选项的使用。如下就是一段 C 语言程序:

// test.c 
#include <stdio.h>
int main(void)
{
	int a = 3;
#ifdef DEBUG
    printf("hello itcast\n");
#endif
    printf("hello world\n");
    return 0;
}
yxm@192:~$ gcc test.c -DDEBUG
yxm@192:~$ ls
a.out  company_project  install  myshare  test.c
yxm@192:~$ ./a.out 
hello itcast
hello world

我们在工作的时候,软件一般有两个版本,一个发行版本(release 版本),一个是调试版本(debug 版本),如果在编译时指定-D参数(如上所示)就相当于是调试版本,可以打印调试信息;如果在编译时不指定-D参数就相当于发行版本。【补充】release 版本,一般要求我们把调试信息、log 信息等输出信息都删除掉,因为频繁的输出会大大降低程序的执行效率。

我们还可以通过 -D 选项设置宏的值,如下例所示:

// test.c
#include <stdio.h>
int main(void)
{
    printf("SIZE: %d\n", SIZE);
    return 0;
}
deng@itcast:~/test$ gcc 2test.c -DSIZE=10
deng@itcast:~/test$ ./a.out
SIZE: 10

多个源文件编译

如果需要编译多个源文件,基本上有两种编译方法,假设有两个源文件为 file1.c 和 file2.c:

  1. 多个文件一起编译
    用法:gcc file1.c file2.c -o file
    作用:将 file1.c 和 file2.c 分别编译后链接成 file 可执行文件。

【注意】此时 file1.c、file2.c 这两个文件没有先后关系,位置随意放置。

  1. 分别编译各个源文件,之后对编译后输出的目标文件进行链接。
    # 用法
    gcc -c file1.c 						# 将file1.c编译成file1.o
    gcc -c file2.c 						# 将test.c编译成file2.o
    gcc -o file1.o file2.o -o file 		# 将file1.o和file2.o链接成file
    # gcc -o *.o -o file 	# 简洁写法,等价于 gcc -o file1.o file2.o -o file
    

总结:以上两种方法相比较,第一种方法编译时需要所有文件重新编译,而第二种方法可以只重新编译修改的文件,未修改的文件不用重新编译

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值