跳转至

GCC编译器详解

概述

GCC(GNU Compiler Collection)是最流行的编译器套件,支持C、C++、Fortran等多种语言。

GCC编译流程

Text Only
源代码(.c) -> 预处理(.i) -> 编译(.s) -> 汇编(.o) -> 链接(可执行文件)

基本编译

单文件编译

Bash
# 编译并运行
gcc hello.c -o hello
./hello

# 分步编译
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         # 链接

# C++编译
g++ hello.cpp -o hello

多文件编译

Bash
1
2
3
4
5
6
7
# 一起编译
gcc main.c func.c -o program

# 分别编译后链接
gcc -c main.c -o main.o
gcc -c func.c -o func.o
gcc main.o func.o -o program

编译选项

优化选项

Bash
1
2
3
4
5
6
7
8
9
-O0     # 不优化(默认)
-O1     # 基本优化
-O2     # 标准优化(推荐)
-O3     # 激进优化
-Os     # 优化代码大小
-Ofast  # 最快执行(不严格遵守标准)

# 示例
gcc -O2 program.c -o program

调试选项

Bash
1
2
3
4
5
6
-g           # 生成调试信息
-ggdb        # GDB专用调试信息
-g3          # 最大化调试信息

# 示例
gcc -g program.c -o program

警告选项

Bash
1
2
3
4
5
6
7
8
-Wall        # 启用所有常见警告
-Wextra      # 额外警告
-Werror      # 警告当作错误
-Wpedantic   # 严格遵循标准
-Wno-unused  # 禁用特定警告

# 示例
gcc -Wall -Wextra program.c -o program

标准选项

Bash
# C标准
-std=c89     # C89/C90
-std=c99     # C99
-std=c11     # C11
-std=c17     # C17
-std=gnu11   # C11 + GNU扩展

# C++标准
-std=c++11   # C++11
-std=c++14   # C++14
-std=c++17   # C++17
-std=c++20   # C++20

预处理器选项

Bash
1
2
3
4
5
6
7
8
-DMACRO           # 定义宏
-DDEBUG=1         # 定义宏并赋值
-UMACRO           # 取消宏定义
-I/path/to/include # 添加头文件路径
-include file.h   # 强制包含文件

# 示例
gcc -DDEBUG -I./include program.c -o program

链接选项

Bash
1
2
3
4
5
6
7
8
9
-L/path/to/lib    # 添加库路径
-lm               # 链接数学库
-lpthread         # 链接pthread库
-static           # 静态链接
-shared           # 生成共享库
-rdynamic         # 导出符号给动态链接器

# 示例
gcc program.c -L./lib -lmylib -lm -o program

创建库文件

静态库

Bash
1
2
3
4
5
6
7
8
9
# 编译目标文件
gcc -c func1.c -o func1.o
gcc -c func2.c -o func2.o

# 创建静态库
ar rcs libmylib.a func1.o func2.o

# 使用静态库
gcc main.c -L. -lmylib -o program

共享库

Bash
# 创建共享库
gcc -shared -fPIC -o libmylib.so func1.c func2.c

# 使用共享库
gcc main.c -L. -lmylib -o program

# 运行时指定库路径
LD_LIBRARY_PATH=. ./program

# 或编译时指定
gcc main.c -L. -lmylib -Wl,-rpath,. -o program

交叉编译

ARM交叉编译

Bash
1
2
3
4
5
6
7
8
# 安装交叉编译器
apt install gcc-arm-linux-gnueabihf

# 交叉编译
arm-linux-gnueabihf-gcc program.c -o program_arm

# 指定sysroot
arm-linux-gnueabihf-gcc --sysroot=/path/to/sysroot program.c -o program

常用架构

Bash
# ARM 32位
arm-linux-gnueabihf-gcc

# ARM 64位
aarch64-linux-gnu-gcc

# RISC-V
riscv64-linux-gnu-gcc

# MIPS
mips-linux-gnu-gcc

链接器控制

链接脚本

Text Only
/* linker.ld */
ENTRY(_start)

SECTIONS
{
    . = 0x40000000;
    .text : { *(.text) }
    . = 0x40010000;
    .data : { *(.data) }
    .bss : { *(.bss) }
}
Bash
# 使用链接脚本
gcc -T linker.ld program.c -o program

属性和内联汇编

函数属性

C
// 对齐
__attribute__((aligned(16))) int var;

// 打包结构
struct __attribute__((packed)) {
    char a;
    int b;
};

// 不返回
__attribute__((noreturn)) void exit_program(void);

// 内联
static inline __attribute__((always_inline)) int add(int a, int b);

// 弱符号
__attribute__((weak)) void handler(void);

内联汇编

C
// 基本内联汇编
asm("movl $1, %eax");

// 扩展内联汇编
int a = 10, b;
asm("movl %1, %0"
    : "=r"(b)
    : "r"(a));

// 带clobber列表
asm("movl %1, %0\n\t"
    "addl $1, %0"
    : "=r"(b)
    : "r"(a)
    : "cc");

编译器内置函数

C
// 位操作
int clz = __builtin_clz(x);      // 前导零计数
int ctz = __builtin_ctz(x);      // 尾随零计数
int pop = __builtin_popcount(x); // 置位计数

// 分支预测
if (__builtin_expect(x, 0)) {    // x大概率是0
    // ...
}

// 内存屏障
__sync_synchronize();

// 原子操作
__sync_add_and_fetch(&var, 1);
__sync_bool_compare_and_swap(&var, old, new);

Makefile集成

Makefile
CC = gcc
CFLAGS = -Wall -O2
LDFLAGS = -lm

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)

program: $(OBJS)
    $(CC) $(OBJS) $(LDFLAGS) -o $@

%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    rm -f $(OBJS) program

调试和分析

生成预处理文件

Bash
gcc -E program.c > program.i

生成汇编文件

Bash
gcc -S program.c -o program.s

查看依赖

Bash
gcc -M program.c        # 所有依赖
gcc -MM program.c       # 忽略系统依赖

参考资料