Linux下GCC教程
环境:Ubuntu 18.04.6
简介:
GCC
是Linux
下的编译工具集,是GNU Compiler Collection
的缩写,包含gcc
、g++
等编译器,该工具及不仅包含编译器,还包含其他工具集,例如ar
、num
等。
GCC工具集不仅能编译C/C++语言,其它例如Objective-C、Pascal、FOrtan、Java、Ada等语言均能进行编译。GCC可以根据不同的硬件平台进行编译,可以进行交叉编译,在A平台上编译B平台上的程序,支持常见的X86、ARM、PowerPC、mips等,以及Linux、Windows等软件平台。
1. 安装
sudo apt update # 更新apt下载源,尽可能保证下载到的软件是最新的
sudo apt install gcc g++ # 同时下载gcc和g++
安装完毕后,查看版本:
gcc -v
g++ -v
2. 工作流程
GCC编译器的工作流程分为四步:
- 预处理:GCC调用预处理器主要完成三件事:
- 展开头文件
- 宏替换
- 去掉注释行
- 编译和优化:GCC调用编译器对文件进行编译,得到汇编文件。
- 汇编:GCC调用汇编器对汇编文件进行汇编,得到二进制文件。
- 链接:GCC调用链接器对程序需要的库进行链接,最终得到一个可执行的二进制文件。
具体如下:
处理前文件名后缀 | 处理 | gcc参数 | 处理后文件名后缀 |
---|---|---|---|
.c/.cpp | 预处理 | -E |
.i |
.i | 编译 | -S |
.s |
.s | 汇编 | -c | .o |
.o | 链接 | 无 | 无固定后缀 |
EG:
-
新建程序:test.c
// 假设程序对应的源文件名为 test.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main() { #ifdef HELLO printf("HELLO宏被定义"); #endif int arr[5] = {1,2,3,4,5}; for(int i=0; i<5; ++i) { printf("arr[%d] = %d\n", i, array[i]); } return 0; }
-
预编译:
gcc -E test.c -o test.i
得到文件形式如下:
// 前面是展开的头文件 int main() { int arr[5] = {1,2,3,4,5}; for(int i=0; i<5; ++i) { printf("arr[%d] = %d\n", i, array[i]); } return 0; }
可以看出:
- 头文件被展开
- 注释被消去
- 宏常量被替换。
-
编译:
gcc -S test.i -o test.s
得到的
test.s
文件内是汇编语言。 -
汇编:
gcc -c test.s -o test.o
得到二进制文件。
-
链接:
gcc test.o -o test
得到可执行文件
test
-
执行test文件,输出如下:
./test arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[3] = 4 arr[4] = 5
3. gcc常用参数
以下表格列出gcc常用参数,这些参数在实际使用中并无顺序之分,只要指定即可。
参数 | 作用 |
---|---|
-E | 对源文件预编译,不进行编译 |
-S | 对源文件进行编译,不进行汇编。 |
-c | 对源文件进行汇编,不进行链接。 |
-o file | 将原文件编译为file |
-I |
指定include头文件的搜索路径 |
-D | 在程序编译时,定义一个宏(在预编译阶段生效,因为该阶段会进行宏替换) |
-w | 不生成任何警告信息。(不推荐,因为有时警告信息就是报错) |
-Wall | 生成所有警告信息。 |
-O[n] | 编译时优化代码。n代表优化的级别,0不优化,1为缺省值,3优化级别最高。 |
-l (小写的l) |
在程序编译时,指定使用的库的名字。要去掉前缀和后缀 |
-L | 指定编译的时候,搜索的库的路径。相对或绝对路径均可 |
-fpic/fPIC | 生成与位置无关的代码(通常动态链接库使用) |
-shared | 生成共享目标文件。通常在建立共享库时。 |
-std | 指定C/C++方言,如-std=c99,gcc默认的方亚是GNU C |
3.1 搜索头文件(-I)
注意是大写的i不是小写的l
编写文件如下:
-
头文件./include/head.h:
#ifndef _HEAD_H #define _HEAD_H // 加法 int add(int a, int b); // 减法 int sub(int a, int b); #endif
-
加法文件:./add.c
#include <stdio.h> #include "head.h" int add(int a, int b){ return a+b; }
-
减法文件:./sub.c
#include <stdio.h> #include "head.h" int sub(int a, int b) { return a-b; }
-
测试文件:./main.c:
#include <stdio.h> #include "head.h" int main() { int a = 10; int b = 5; printf("a+b=%d\n", add(a, b)); printf("a-b=%d\n", sub(a,b)); return 0; }
最终文件结构如下:
.
├── add.c
├── cal
├── include
│ └── head.h
├── main.c
└── sub.c
此时使用gcc进行编译,发现无法找到头文件head.h
。头文件寻找策略如下:
- 在c/c++中,头文件如果使用
<>
,那就是从默认库中寻找(linux默认在/usr/lib
) - 如果是
""
包裹,那就从当前目录下寻找。
而头文件在include
下,自然找不到,因此要在编译时指定头文件目录:
gcc *.c -o cal -I include/
编译成功。
3.2 定义宏(-D)
在编译程序时我们可以额外指定一个宏,这个宏会被写入文件中,从而对文件进行一定的控制。该指令实际在预编译阶段使用,因为该阶段会展开宏。
EG:
-
定义程序如下:
#include <stdio.h> #define NUMBER 3 int main() { #ifdef DEBUG printf("这是一条调试信息\n"); #endif for (int i = 0; i < NUMBER; i++) { printf("%d\n", i); } return 0; }
-
不指定宏进行编译:此时不会输出调试语句
gcc test.c -o test.o ./test.o 0 1 2
-
指定宏进行编译:
gcc test.c -o test.o -D DEBUG ./test.o 这是一条调试信息 0 1 2
使用场景:
用于控制调试语句,将所有调试语句根据宏的存在与否判断是否编译,这样就可以在灵活控制程序中调试信息输出与否。
4. gcc与g++
二者之间的区别如下:
- 在编译阶段(即预编译之后的阶段):
- 后命名为
.c
的文件,gcc将其视作C程序,而g++将其视作C++程序。 - 后缀名为
.cpp
的文件,gcc和g++都会将其视作C++程序。 - 在该阶段g++会调用gcc对文件进行编译。也就是说编译阶段只会由gcc完成。
- 后命名为
- 在链接阶段(最后一个阶段):
- gcc和g++都能连接到标准C库。
- g++可以自动链接到C++标注库,而gcc想要连接到C++标准库需要参数
lstdc++
- 关于
_cplusplus
宏的定义- g++会自动定义该宏,但这并不影响其对于c文件的编译。
- gcc需要根据后缀名来确认是否需要定义该宏。
综上:
- gcc和g++都能编译C文件
- g++能直接编译C++文件,而gcc需要添加参数
-lstdc++
- gcc和g++都能定义宏
_cplusplus
EG:
# 编译c程序
gcc test.c -o test
g++ test.c -o test
# 编译c++程序
g++ test.cpp -o test
gcc test.cpp -o test -lstdc++
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)