你在以前遇到了什么困难你又是如何解决的?

1.配置环境

1. 情境 (Situation)

最近我在学习音视频开发,尝试运行一个基于 Qt 和 FFmpeg 的开源播放器项目(HPlayer)。这个项目比较老,是基于 Qt 5.9 和 MSVC 编译器 开发的,而我的开发环境是 Qt 6 和 MinGW 编译器。"

2. 任务 (Task)

"我的目标是解决环境不兼容的问题,让这个项目在我的 MinGW 64-bit 环境下成功编译并运行。

3. 行动 (Action)

"我遇到了三个阶段的阻碍,我是这样一步步解决的:

  • 第一步:版本迁移与预处理修复

    首先我发现 Qt 6 删除了很多 Qt 5 的模块,导致大量报错。我决定降级安装 Qt 5.12 LTS 以保证兼容性。在编译初期,遇到了 sprintf 未定义的错误,我通过引入 <cstdio> 头文件解决了预处理阶段的问题。

  • 第二步:解决编译器 ABI 不兼容(链接阶段)

    这是最棘手的部分。项目自带的第三方库(FFmpeg 和 SDL2)是 MSVC 格式(.lib) 的,而我使用的是 MinGW 编译器,导致链接器报错 undefined reference 和 cannot find -lSDL2。

    我意识到这是二进制接口(ABI)不兼容 导致的。我没有修改源码逻辑,而是去下载了 MinGW 版本的 FFmpeg 和 SDL2 开发包 ,替换了原来的库文件。

    同时,我重写了 .pro 构建脚本(QMake),修正了库文件的搜索路径(-L)和链接顺序(MinGW 对库的链接顺序敏感)。

  • 库也是不同编译器编译出来的,如果编译器换了,那么原本用的库也要换成不同版本的。

  • 第三步:解决运行时依赖

    编译成功后,程序启动即崩溃。通过分析,我判断这是动态链接库(DLL)缺失。静态链接阶段虽然通过了,但运行时操作系统找不到对应的 .dll。我手动将 FFmpeg 和 SDL2 的 DLL 文件复制到了可执行文件目录下,程序最终完美运行。"

4. 结果 (Result)

  • "最终,我成功复活了这个老项目。通过这次经历,我不仅熟悉了 Qt 的构建系统,更深刻理解了 C++ 从预处理、编译到链接的全过程 ,特别是 静态库与动态库在 Windows 下不同编译器之间的差异。"

5.总结

c++运行的整个流程是预处理,编译,链接和运行。

1. 预处理 (Preprocessing) ------ "整理手稿"

这是流水线的第一步,编译器(cc1/cl)还没真正开始工作 ,主要是预处理器在干活。

  • 输入:源代码文件 (.cpp) + 头文件 (.h)。

  • 动作纯文本替换。它不检查语法,只认以 # 开头的指令。

    • #include (展开) :把头文件的内容原封不动地复制粘贴到 .cpp 文件里。

    • #define (替换):把代码里的宏(比如 #define PI 3.14)全部替换成具体的值。

    • #ifdef (过滤):根据条件决定哪些代码要保留,哪些要删掉(比如 Windows 下删掉 Linux 的代码)。

  • 输出:一个巨大的、没有任何 # 指令的纯 C++ 文件(通常叫"翻译单元")。

  • 为什么需要这一步?

    • 为了代码复用。如果没有 #include,你每次想用 printf 都得把那几千行声明重新敲一遍。

    • 为了跨平台。通过 #ifdef,一份代码可以在 Windows 和 Linux 上表现出不同的行为。


2. 编译 (Compilation) ------ "翻译成汇编"

这是最复杂、最"聪明"的一步。

  • 输入:预处理完的纯 C++ 代码。

  • 动作语法分析 & 翻译

    • 查错:检查你有没有少分号,变量类型对不对。如果有错,直接报错停止。

    • 优化:编译器会分析你的逻辑,去掉没用的代码,或者把复杂的计算简化(比如把 a = 2 + 3 直接改成 a = 5)。

    • 翻译 :把 C++(高级语言)翻译成 汇编语言(低级助记符)。

  • 输出:汇编文件 (.s 或 .asm)。里面是类似 MOV EAX, 1 这种指令。

  • 为什么需要这一步?

    • 因为 C++ 太高级了,CPU 看不懂。

    • 汇编语言是人类和机器之间的桥梁,它比二进制可读性强,方便调试和优化。

比喻:翻译官把中文手稿翻译成了英文(汇编)。同时修正了原来的语病(语法错误),并把啰嗦的句子改写得更简练(优化)。


3. 汇编 (Assembly) ------ "转成机器码"

这一步比较机械化,由 汇编器 (Assembler) 完成。

  • 输入:汇编文件 (.s)。

  • 动作查表翻译

    • 把汇编指令(如 ADD)一一对应地转换成 CPU 能直接执行的二进制指令(如 01001101)。
  • 输出目标文件 (Object File),在 Windows 下是 .obj,Linux/MinGW 下是 .o。

  • 为什么需要这一步?

    • CPU 是电路做的,它只认识高低电平(0 和 1)。这一步就是把人类的逻辑彻底变成机器的逻辑。

    • 注意 :此时生成的 .o 文件是缺胳膊少腿的。它知道"我要调用 printf",但它不知道 printf 在内存的哪个地址,只是留了个空位。

比喻:排版员把英文手稿变成了莫斯密码(二进制)。现在你有了一堆散乱的、看不懂的密码纸,每一张纸对应一个 .cpp 文件。


4. 链接 (Linking) ------ "装订成书"

这是最后一步,也是最容易出"找不到文件"错误 的一步。由 链接器 (Linker) 完成。

2. 动态链接 (Dynamic Linking) ------ 引用
  • 输入

    • 你自己生成的一堆 .o 文件。

    • 第三方库文件(.lib / .a)。

  • 动作地址回填 & 合并

    • 合并:把你所有的 .o 文件拼在一起。

    • 找人:当 main.o 里说"我要调用 play()"时,链接器会去其他的 .o 或 .lib 库里找 play() 到底在哪。

    • 回填:找到后,把 play() 在内存中的真实地址填回到 main.o 的那个空位上。

  • 输出 :最终的 可执行文件 (.exe)。

  • 为什么需要这一步?

    • 模块化开发:我们不可能把几百万行代码写在一个文件里。链接器允许我们把代码拆分成很多个文件分别编译,最后再组装起来。

    • 使用库:我们可以直接使用别人写好的功能(如 SDL2, FFmpeg),而不需要知道它们是怎么实现的,只要在最后一步把它们链接进来即可。

1. 静态链接 (Static Linking) ------ 抄书
  • 做法 :在编译(链接)的时候,把百科全书里你用到的那几页内容,原封不动地抄写(复制)进你的文章里。

  • 结果

    • 你的文章(.exe)会变厚(体积大)。

    • 发布的时候,只需要给别人这一本书就够了,不需要附带百科全书。

    • 文件名特征:Linux/MinGW 下通常叫 .a (Archive),Windows MSVC 下叫 .lib。

  • 做法 :你不在文章里抄内容,而是写上一句:"此处详见《FFmpeg百科全书》第50页"

  • 结果

    • 你的文章(.exe)很薄(体积小)。

    • 但是,发布的时候,你必须把《FFmpeg百科全书》(.dll)也一起打包给别人。如果只有文章没有书,读者读到这一行就会崩溃(Crash)。

    • 文件名特征:Windows 下叫 .dll (Dynamic Link Library)。

相关推荐
西岸行者3 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意3 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码3 天前
嵌入式学习路线
学习
毛小茛3 天前
计算机系统概论——校验码
学习
babe小鑫3 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms3 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下3 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。3 天前
2026.2.25监控学习
学习
im_AMBER3 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J3 天前
从“Hello World“ 开始 C++
c语言·c++·学习