二十行程序、一条 gcc 命令,难点全在那条命令背后的 pkg-config。 四种报错对应四个病根:编译器、头文件、库路径、版本不匹配。
1.3 把库装好了,这一篇终于写出第一个自己的程序。 第一章到此结束,第二章开始认七个库各自管什么。
一、二十行的第一个程序
程序本身没什么可讲的,三行有效代码。 行 作用
#include
它的价值在于「最小可验证」:只依赖一个库、一个函数、一个符号。能编过,说明头文件路径对了; 能链上,说明库路径与库名对了;能跑出正确的号,说明 DLL 也对了。三层一次验完。 版本号还能当尺子用。 打出来是 62 开头,就是 8.0 系;是 59 开头,就是 5.0 系。 跟你以为装的版本对不上,说明机器上有两套。
二、pkg-config 到底做了什么
它干的事一句话:把 .pc 文件里的路径拼成 -I 与 -L -l。
参数 输出什么
--cflags -I/usr/local/ffmpeg/include --libs -L/usr/local/ffmpeg/lib -lavcodec -l... --modversion 库的版本号 --exists 在就回 0,不在回 1
所以 gcc ver.c $(pkg-config --cflags --libs libavcodec) 等于手写了那一串。 手写容易错,
pkg-config 不会错——前提是 .pc 文件在。
.pc 文件在哪儿? --prefix 目录下的 lib/pkgconfig/。
所以装完 FFmpeg,那个目录必须进 PKG_CONFIG_PATH,否则 pkg-config 说「没这个包」。
本机 PKG_CONFIG_PATH 是 0 段,pkg-config 也不在 PATH——这条路本机根本不存在。
三、四种报错,四个病根
同一个「编不出来」,四种原因,报错关键词完全不同。 报错关键词 病根 去哪看
FileNotFoundError 编译器不在 PATH 环境变量 avcodec.h: No such file 头文件路径没给 -I 或 cflags cannot find -lavcodec 库路径或库名错 -L 或 -l undefined reference 头文件与库版本不符 装了两套
第三种与第四种的差别值得记住:前者是「找不到库文件」,后者是「找到了库但没有这个符号」。
前者看路径,后者看版本。
本机 Demo 真跑了一次 gcc --version,得到 FileNotFoundError。
WinError 码是 2,意思是「这个文件不存在」。
这就是第一行的样子:不是编译失败,是连编译器的门都没进去。
四、编过了跑不起来:运行期路径
编得过、跑不动,报 error while loading shared libraries: libavcodec.so.62。
因为链接期与运行期是两套机制:链接期看 -L,运行期看 LD_LIBRARY_PATH 与 ldconfig。
做法 生效范围 适合什么
export LD_LIBRARY_PATH=... 当前 shell 临时测试 写进 /etc/ld.so.conf.d/ 全系统 正式部署 编进 rpath 这个程序 发版
ldd 你的程序 | grep avcodec 能立刻看出找没找到。 回 not found 就是路径没设,
回一个路径就说明找到了。
这三个办法里,rpath 最适合发版:路径写进可执行文件,换台机器只要有同样的目录结构就能跑,
不依赖环境变量。
五、动手 Demo:GCC 编一个调 FFmpeg 的小程序——本机编不了,但错在哪看得见
本机没有 gcc,真跑会得到 FileNotFoundError。 这一段把这个失败原样打出来,再摆四种报错的分类表。
import subprocess
try:
r = subprocess.run(["gcc", "--version"], capture_output=True)
print(r.returncode)
except FileNotFoundError as e:
print("WinError", e.winerror)
实跑输出:
--------------------------------------------------------------------
GCC 编一个调 FFmpeg 的小程序——本机编不了,但错在哪看得见
--------------------------------------------------------------------
**① 要编的东西只有二十行**
`#include <stdio.h>`
`#include <libavcodec/avcodec.h>`
`int main(void) {`
` printf("%u\n", avcodec_version());`
` return 0;`
`}`
**② 编译命令**
`gcc ver.c -o ver $(pkg-config --cflags --libs libavcodec)`
`pkg-config` 干的事就一件:把 `-I` 与 `-L -l` 那串东西拼好交给你,省得手写路径。
它靠 `.pc` 文件工作,所以编 FFmpeg 时那个 `--prefix` 必须和 `.pc` 文件里的路径一致。
**③ 本机真跑一次,看它怎么失败**
调 `gcc --version` → **FileNotFoundError**,WinError = 2
这就是「工具链没装」在代码里的样子:
**不是编译失败,是连编译器的门都没进去。** `FileNotFoundError [WinError 2]` 这条错误,
以后见了就知道要在 PATH 上找问题,而不是在源码里找问题。
**④ 真机上的四种结局,一眼分类**
报错关键词 真正的病根
---------------------------------------------------------------
FileNotFoundError WinError 2 编译器不在 PATH
libavcodec/avcodec.h: No such file 头文件路径没给
cannot find -lavcodec 库路径或库名错
undefined reference to av_xxx 头文件与库版本不匹配
**第五种最隐蔽**:头文件与库版本不一致时,编译能过、链接报一堆「未定义引用」。
原因是用新头文件编、拿了老 `.so` 链。
所以 `--prefix` 那套东西装完,要把旧版本从 `LD_LIBRARY_PATH` 里清掉。
**⑤ 一条验完的命令**
`pkg-config --modversion libavcodec` 报不出号,就说明 `.pc` 没进`PKG_CONFIG_PATH`;
报出来了但版本比头文件旧,就是装了两套。本机 `PKG_CONFIG_PATH` 是 0 段——连这条路都不存在。
WinError 2 就是「这个文件不存在」。 看见它,问题在 PATH,不在源码。 真机上这一句会回一个版本号,而不是异常。
30 秒实验
pkg-config --exists libavcodec; echo $?看它在不在。ldd ./ver | grep av看运行时依赖找到没。- 把
-I故意删掉,看报错是不是avcodec.h: No such file。
常见误区
- 手写
-I与-L路径。 一装新版本就全错,用 pkg-config。 - 编过了就以为能跑。 运行期还要 LD_LIBRARY_PATH 或 rpath。
- 两套 FFmpeg 装在同一台机器上。 头文件与库对不上,报一堆未定义引用。
自测三问(点开对答案)
1. `pkg-config --cflags --libs libavcodec` 输出什么? —— `-I` 头文件路径与 `-L -l` 库路径库名,直接交给 gcc 就能用。 2. `cannot find -lavcodec` 与 `undefined reference to av_xxx` 有什么区别? —— 前者是找不到库文件(路径问题),后者是找到库但没这个符号(版本不匹配)。 3. 编得过却跑不起来,报找不到 .so,怎么修? —— 设 LD_LIBRARY_PATH、写 ld.so.conf.d、或编译时用 rpath 把路径写进可执行文件。- ☐ (本章完,下一节 2.1 FFmpeg 八大核心开发库)