2. GCC入门
说到C/C++编译,GCC绝对是绕不开的基石。我最早接触GCC是在大学做嵌入式开发的时候,那时候觉得它就是个黑盒子,敲个gcc hello.c就能跑。后来踩了不少坑,才慢慢摸透它的脾气。今天咱们就把它掰开揉碎了聊聊。
2.1 GCC的安装
不同平台安装方式不太一样,我分别说说。
Linux(Ubuntu/Debian)
sudo apt update
sudo apt install build-essential
这条命令会把GCC、G++、make等工具链一次性装好。我个人习惯装完后跑一下gcc --version确认版本。
macOS
xcode-select --install
或者用Homebrew:
brew install gcc
注意macOS自带的clang有时候会被软链接成gcc,其实不是真正的GCC。我建议用gcc-13这样的具体版本号来调用。
Windows
推荐用MinGW-w64。下载安装后记得把bin目录加到环境变量PATH里。嗯,这里要注意:别装错版本,64位系统选x86_64,32位选i686。
gcc -v,能看到详细的版本信息和配置参数。我经常用这招确认当前用的是哪个编译器。
2.2 基本用法:从源码到可执行文件
先写个最简单的程序:
// hello.c
#include <stdio.h>
int main() {
printf("Hello, GCC!\n");
return 0;
}
编译它:
gcc hello.c -o hello
然后./hello就能看到输出了。就这么简单?其实背后GCC干了四件事:预处理、编译、汇编、链接。我刚开始学的时候觉得没必要了解这些细节,直到有一次遇到链接错误,折腾了两天才明白是库没链接对。
咱们可以分步看看:
gcc -E hello.c -o hello.i # 预处理
gcc -S hello.i -o hello.s # 编译成汇编
gcc -c hello.s -o hello.o # 汇编成目标文件
gcc hello.o -o hello # 链接
实际开发中很少这么干,但调试复杂问题时会用到。比如想看宏展开结果,就用-E。
2.3 常用编译选项详解
这部分是重点,我按使用频率排序来讲。
-O 优化选项
控制编译器的优化程度。有这几个级别:
| 选项 | 含义 | 编译时间 | 执行速度 |
|---|---|---|---|
| -O0 | 不优化(默认) | 最快 | 最慢 |
| -O1 | 基本优化 | 较快 | 较快 |
| -O2 | 更多优化 | 较慢 | 很快 |
| -O3 | 激进优化 | 最慢 | 最快 |
| -Os | 优化体积 | 中等 | 中等 |
我个人习惯:开发调试用-O0,发布用-O2。-O3有时候会引入一些奇怪的bug,比如循环展开导致的多线程问题。我曾经在一个音频处理项目里用-O3,结果声音出现了爆音,查了两天才发现是优化过度导致时序变了。
-O0。
-g 调试信息
生成调试符号表,让GDB能定位到源码行号、变量名等。
gcc -g hello.c -o hello
我建议:只要不是最终发布版本,都加上-g。它不会影响执行速度,只是让可执行文件变大一点。而且就算发布了,也可以保留一份带调试信息的版本,线上出问题能快速定位。
-Wall 警告选项
开启所有常见的警告。注意是「所有常见」的,不是「所有」的。
gcc -Wall hello.c -o hello
还有更严格的:
gcc -Wall -Wextra -Werror hello.c -o hello
-Werror会把所有警告当成错误处理。我刚开始觉得这太苛刻了,直到有一次线上崩溃是因为一个未初始化的变量。从那以后,我的项目都强制开启-Werror。
-Wall -Wextra -Werror写进Makefile或CMakeLists.txt里。这样谁提交的代码有警告,编译就直接失败,省得代码审查时扯皮。
其他常用选项
-std=c11:指定C语言标准。我一般用-std=c11或-std=c17。-I:添加头文件搜索路径。比如-I./include。-L:添加库文件搜索路径。比如-L./lib。-l:链接指定库。比如-lm链接数学库。-D:定义宏。比如-DDEBUG相当于在代码里写了#define DEBUG。
举个例子,综合使用:
gcc -Wall -Wextra -O2 -g -std=c11 -I./include -L./lib -lm main.c -o app
这条命令的含义是:开启严格警告、二级优化、带调试信息、使用C11标准、指定头文件和库路径、链接数学库。
2.4 知识体系总览
下面这张图是我整理的GCC入门知识结构,方便你对照着学:
2.5 避坑指南
最后分享几个我踩过的坑:
- 忘记链接数学库:用了
sqrt()、sin()这些函数,但没加-lm。编译不会报错,链接时报一堆未定义引用。我第一次遇到时懵了半天。 - 头文件路径不对:用了第三方库,但没加
-I指定路径。报错fatal error: xxx.h: No such file or directory。检查一下路径是不是写对了。 - 优化导致调试异常:用
-O2调试,断点乱跳,变量值显示不对。改成-O0就好了。 - 32位和64位混用:在64位系统上编译32位程序,需要加
-m32,而且得装32位的库。我有个项目因为这个在客户机器上跑不起来,被骂了一顿。
make,省得记一堆参数。后面章节会详细讲Makefile的用法。
好了,GCC入门就聊到这儿。这些选项你多用几次就熟了,关键是理解每个选项背后的含义。下次遇到编译问题,先想想是不是优化级别不对,或者警告没开全。