GCC编译
本笔记为作者再学习嵌入式Linux的一些心得体会,如有不对的地方,请包含与谅解!我主要是采用香橙派5来作为我们学习嵌入式Linux的环境。
------------by wsoz
Hello程序代码
c
#include <stdio.h>
int main(int argc,char **argv)
{
if(argc > 1)
{
printf("Hello, %s!\n", argv[1]);
}
else
{
printf("Hello, World!\n");
}
return 0;
}
代码编译过程如下:

此处我们先对程序做一个简单的介绍:
c
int main(int argc,char**argv)
这个是C程序入口标准函数声明
int作为返回值- 0--表示程序正常结束
- 非0--出错或者异常
int argc表示命令行参数个数- 包含了程序名/路径本身
char **argv表示的是命令行参数,每个参数都是字符串- 比如输入
./hello world,会输出hello world! - 其中参数
argv[0]:程序名/路径 argv[1]:world
- 比如输入
GCC编译四阶段
| 阶段 | 命令示例 | 输入 | 输出 | 作用说明 |
|---|---|---|---|---|
| 预处理 (Preprocessing) | gcc -E hello.c -o hello.i |
.c 源文件 |
.i 预处理文件 |
• 展开所有宏定义(#define) • 处理#include指令,插入头文件内容 • 删除所有注释 • 处理条件编译(#ifdef等) • 添加行号和文件标识 |
| 编译 (Compilation) | gcc -S hello.i -o hello.s |
.i 预处理文件 |
.s 汇编文件 |
• 将C代码翻译成汇编代码 • 进行语法检查和语义分析 • 进行代码优化 • 生成目标平台的汇编指令 |
| 汇编 (Assembly) | gcc -c hello.s -o hello.o |
.s 汇编文件 |
.o 目标文件 |
• 将汇编代码转换为机器码 • 生成二进制目标文件 • 包含符号表和重定位信息 • 此时还不能直接执行 |
| 链接 (Linking) | gcc -o hello hello.o |
.o 目标文件 |
可执行文件 | • 链接所需的库文件(如libc.so) • 解析符号引用 • 重定位代码和数据段 • 生成最终可执行程序 |
💡 一步完成所有阶段:
bashgcc -o hello hello.c # 自动完成预处理→编译→汇编→链接
GCC 常用编译选项
| 常用选项 | 描述 |
|---|---|
| -E | 预处理 |
| -S | 编译 |
| -c | 汇编 |
| -o | 指定输出文件 |
| -I(大小i) | 指定头文件目录 |
| -L | 指定链接时库文件目录 |
| -l(小写L) | 指定链接哪一个库文件 |
| -v | 详细查看编译过程 |
多文件代码示例
c
#include <stdio.h>
#include "name.h"
int main(int argc,char **argv)
{
if(argc > 1)
{
printf("Hello, %s!\n", argv[1]);
}
else
{
printf("Hello, World!\n");
}
fun();
return 0;
}
c
#include <stdio.h>
void fun(void)
{
printf("I am WSOZ\r\n");
}
此时多文件进行编译的话我们通常可以分成两种方法进行
- 方法1
bash
gcc -o hello hello.c name.c
直接一步到位,直接包含所有的文件即可,简单快速
- 方法2
bash
#分开汇编
gcc -c hello.c -o hello.o
gcc -c name.c -o name.o
#统一链接
gcc -o hello hello.o name.o
我们对多程序的情况下一般采取分开汇编,然后统一进行链接,此种方法在有少数文件需要修改的情况更加适用
动态库和静态库
库(Library)的作用:把通用功能封装起来复用,避免每个项目都重复写代码。
静态库(.a)作用
在链接阶段把库代码"拷贝"进你的可执行文件里。
- 优点:部署简单(一个程序就能跑)、不怕目标机缺库
- 缺点:可执行文件变大;多个程序各带一份,浪费存储/内存;库升级要重新编译程序
动态库(.so)作用
程序里只保留"引用",运行时再加载库。
- 优点:可执行文件更小;多个程序可共享同一个库;升级库更方便
- 缺点:运行依赖库文件和版本(可能出现"找不到so"或版本不匹配)
制作与使用库
对于库的制作,本质上就是将编译后的 .o 打包成对应格式,然后在链接阶段复用。
-
第一步生成目标文件
.o(动态库建议加-fPIC)bashgcc -fPIC -c name.c -o name.o -
第二步打包成静态库/动态库
bash# 动态库 gcc -shared -o libmy.so name.o # 静态库 ar crs libmy.a name.o
下面就是对于库的使用了,大致有两种方法
方法1:把库放到系统目录(如 /usr/local/lib)
动态库和静态库都可以放进去;只是链接/运行行为不同。
bash
# 安装到系统库目录(推荐 /usr/local/lib)
sudo cp libmy.so /usr/local/lib/
sudo cp libmy.a /usr/local/lib/
sudo ldconfig
1)使用动态库(.so)
bash
gcc -o hello hello.c -lmy
./hello
2)使用静态库(.a)
bash
# 最直接:显式写静态库文件路径
gcc -o hello hello.c /usr/local/lib/libmy.a
./hello
方法2:不放系统目录,通过 -L 指定路径
1)使用动态库(.so)
bash
# 链接阶段找到库
gcc -o hello hello.c -L. -lmy
# 运行阶段还要能找到 .so(2选1)
LD_LIBRARY_PATH=. ./hello
# 或者
gcc -o hello hello.c -L. -lmy -Wl,-rpath,'$ORIGIN'
./hello
2)使用静态库(.a)
bash
# 最直接:显式写静态库文件路径
gcc -o hello hello.c ./libmy.a
./hello
关键区别
-L只影响链接阶段;.so运行时还要能被系统找到;.a在链接时已拷贝进可执行文件,运行时不再依赖该库文件。
bash
# 检查可执行文件依赖
ldd ./hello