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

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)。

相关推荐
我命由我123452 小时前
Android Studio - Android Studio 中的 View Live Telemetry
经验分享·学习·android studio·学习方法·android jetpack·android-studio·android runtime
xiaoxiaoxiaolll2 小时前
面向集成微系统供电:《Light》揭示石墨烯混合材料微型电容器的结构化电极设计与性能优化
学习
roo_12 小时前
JAVA学习-MAC搭建java环境和spring boot搭建
java·学习·macos
詩不诉卿2 小时前
Zephyr学习之PWM方式驱动LED灯记录
学习
枷锁—sha2 小时前
【PortSwigger Academy】SQLi UNION 攻击 (确定列数)
服务器·数据库·学习·安全·网络安全
1379号监听员_2 小时前
PID学习笔记
笔记·学习
Tina Tang3 小时前
Agentic AI学习笔记(2)
笔记·学习
_李小白3 小时前
【Android GLSurfaceView源码学习】第三天:GLSurfaceView的Surface、GLES与EGLSurface的关联
android·学习
m0_748240443 小时前
ThinkPHP框架学习全攻略
学习