走进Qt--工程文件解析与构建系统

在Qt开发中,构建系统如同项目的"骨架",它决定了:

  • 代码如何编译成可执行文件
  • 资源文件(如图片/UI)如何被嵌入
  • 跨平台时的编译一致性

本文将深入解析Qt构建工具(qmake/CMake)的工作机制,并揭示从.pro文件到最终可执行文件的完整诞生过程...

1.常见构建系统概述

在前面一篇文章中我们使用的是qmake构建项目,那么接下来就简单讲讲Qt的常见构建方式。

qmake

qmakeQt官方早期推荐的构建工具,语法简单。核心定位是专门为Qt项目设计,适合中小型Qt项目快速搭建。qmake通过.pro文件描述项目结构,自动生成Makefile文件,用于后续makenmake编译。而make,是一种任务执行工具,它根据Makefile中写好的指令,去执行各种任务(主要是编译和链接)。Makefile就是一堆规则说明,而make就是读这些规则去干活。

简单的例子,当新建一个Makefile文件,其中内容可以是这样的:

main:main.cpp g++ -o main mian.cpp

在命令行直接执行make就可以直接执行Makefile,进而执行Makefile中的命令来编译出可执行文件。

CMake

CMake是跨平台通用构建工具,近年来Qt官方逐步推荐CMake,特别是Qt6以后。CMake支持复杂的工程组织、模块依赖管理,以及现代IDE(如Visual Studio)更好集成,适合大型和跨平台项目。

其他构建工具

GN

GN(Generate Ninja),也可以说Google高性能构建系统,由Google开发,主要配合Ninja构建系统使用,特点是极致的构建速度,通常用于大型项目(例如 Chromium 浏览器),但生态局限。属于高级用法。

QBS

Qt公司开发的构建工具,目的是取代qmake,但推广受限,使用率较低。

2.qmake项目的基本文件

在本小节,通过一个简单的Qt工程项目简单介绍qmake项目文件说明和其他细节。

2.1 新建Qt工程

打开Qt Creater,在【欢迎】模式下,点击【创建项目...】

在Qt中,Widget类是所有窗口类的基类,我们要创建一个窗口应用,选择【Application(Qt)】-->【Qt Widget Application】-->【选择...】

紧接着给项目命名与设置项目保存位置,接下来就是选择构建方式Define Build System,这里选择qmake-->【下一步】

接下来就是选择窗口类,其中有三个选项,分别是QMainWindowQWidget以及QDialog。其中QDialogQMainWidow都继承自QWidgetQWidget为另外两个窗口类的基类。这里我们默认QMainWindow。选择完毕,可以给类名重命名,这里按默认名称继续。

3个窗口类的区别

QMainWindow:包含菜单栏、工具栏、状态栏

QWidget:一个普通窗口,没有菜单栏、状态栏

QDialog:对话框

在Translation File选择框中,这里会有语言选项,这里提供其他语言开发选项;这里选择【无】/【none】

在构建套件选择中,这里选择MinGW64即可,接下来就是版本控制选项,版本控制主要有两种svn和git这里不做配置,在该选择框最下方会看到我们项目创建的文件。默认选择【完成】

点击运行,弹出默认窗口,项目创建完毕。接下来为各个文件的说明。

2.2 .pro文件

.proqmake识别的工程配置文件;指定了源码文件、头文件、需要链接的模块(如coregui等)、编译参数等信息。

打开.pro文件,会看到这样的信息

接下来,自上而下详细解释该文件的主要内容:

1.QT += core gui

这一行声明了需要用到的Qt模块:

  • core模块:提供了Qt的基本功能,比如信号与槽、字符串、容器、时间管理等。
  • gui模块:提供了图形界面功能,比如窗口、图形绘制、事件处理等

后期如进行数据库开发,就需要添加sql模块;进行网络通信如TCPUDPHTTP通信,就需要添加network模块,等等。

2.greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

这一行做了一个条件判断:

如果Qt版本大于4,就需要额外加上widgets模块。因为从Qt5开始,Widgets(传统控件界面)被单独拆分到了widgets模块里。也就是说,在Qt5/Qt6下开发传统桌面应用,需要加上这句,否则界面控件(如按钮、窗口)无法使用。

3.CONFIG += c++17

设置项目使用的C++标准为C++17

Qt6要求至少是C++17标准,因此加上这一行是比较常见的。

如果要使用C++20,可以改成

CONFIG += c++20

4.注释部分

ini 复制代码
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

这段时提示你,如果希望禁止使用已启用的API(比如Qt5里已经废弃的功能),可以取消注释这一行。

这样,编译器在遇到废弃的API时,会直接报错,帮助你编写更现代、更规范的代码。

5.SOURCES += \main.cpp \mainwindow.cpp

这里列出所有的源文件.cpp),qmake 根据这里的列表来编译生成目标文件。

  • main.cpp :程序入口。
  • mainwindow.cpp :主窗口类的实现。

\ 是换行符号,表示后面还有内容,保持文件可读性。

6.HEADERS += \mainwindow.h

列出头文件.h 文件),主要是为了方便 IDE 自动管理依赖关系,并且在一些生成 moc 文件(信号槽)时需要头文件参与。

7.FORMS += \mainwindow.ui

列出界面描述文件.ui 文件)。

  • 这些 .ui 文件在编译时由 uic 工具自动转换成对应的C++代码,参与最终编译。

8.部署规则(qnx/unix)

这一段是针对不同平台做的安装部署路径设置。

解释一下

qnx: target.path = /tmp/$${TARGET}/bin

如果编译的是QNX系统(一种嵌入式实时操作系统),则将可执行文件安装到/tem/项目名/bin

else: unix:!android: target.path = /opt/$${TARGET}/bin

否则,如果是普通的Unix/Linux系统 (但不是Android),就安装到/opt/项目名/bin

!isEmpty(target.path): INSTALLS += target

如果设置了target.path,就将其加入到INSTALLS变量,这样make install时可以自动复制到目标路径。

2.3 main.cpp

这个文件是程序的入口点,在这个文件中你会看到如下内容

cpp 复制代码
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

逐行详细解释

1.#include "mainwindow.h"

这一行的作用是:

  • 引入我们自己定义的主窗口类 MainWindow 的声明文件。
  • mainwindow.h 中通常定义了主窗口的构造、析构函数,以及窗口的基本逻辑。

为什么要引入? 因为后面 MainWindow w; 要创建对象,必须知道 MainWindow 是什么。

2.#include<QApplication>

这一行是:

  • 引入 Qt 的 应用程序管理类 QApplication
  • QApplication 是 GUI 应用程序的基础,必须创建一个它的实例,才能启动窗口系统、消息循环等。

注意:

  • 对于图形界面应用程序 (有窗口的),必须用 QApplication
  • 如果是纯控制台应用程序 ,可以用 QCoreApplication,少了图形相关功能。

3.int main(int argc, char *argv[])

这是标准的 C++ 程序入口:

  • argcargument count,命令行参数的个数。
  • argvargument vector,存储命令行参数的字符串数组。

Qt为什么保留这两个参数? 因为 Qt 也支持在启动时接受命令行参数,比如指定配置文件路径、启用调试模式等,所以需要把参数传递给 QApplication

4.QApplication a(argc, argv);

创建一个 QApplication 对象 ,叫做 a

  • 它负责整个程序的生命周期管理,包括:窗口调度、事件处理、系统交互等。
  • 必须在创建任何窗口控件之前创建它。

注意:

  • 这个 a 必须是 main() 里的局部变量,而且只能有一个。
  • argcargv 被传进去,用来让 Qt 解析程序启动参数。

5.MainWindow w;

创建一个 MainWindow 类型的对象 w

  • MainWindow 是我们自己定义的主窗口类 ,通常是继承自 QMainWindowQWidget
  • MainWindow 类中,可以添加按钮、标签、菜单栏等控件,定义主界面内容。

为什么直接写在栈上(不是 new 出来)? 因为 Qt 对象有自己的父子对象机制,生命周期管理很好,局部变量在 main() 退出时自动销毁即可,不用担心内存泄漏。

6.w.show();

调用主窗口对象的 show() 方法,让窗口显示在屏幕上

  • 如果不调用 show(),窗口是不会显示的。
  • show() 方法会向操作系统注册窗口、绘制界面。

注意: 如果你想在窗口显示前做一些初始化(比如设置大小、标题),可以在 show() 之前调用相关方法。

7.return a.exec();

  • a.exec() 进入 Qt 的事件循环(Event Loop)。
  • 程序开始监听用户操作(比如点击、键盘输入)、系统事件(比如窗口关闭)等。

这个函数会一直阻塞 在这里,直到所有窗口关闭,才会退出事件循环,继续执行 return,程序结束。

简单理解 就是:a.exec() 启动了整个应用的心跳,支撑起所有交互。

2.4 界面文件和源文件(mainwindow.h / mainwindow.cpp

mainwindow.h :声明主窗口类(通常继承自 QMainWindowQWidget)。

mainwindow.cpp:定义主窗口的具体行为,如按钮点击响应、窗口布局初始化等。

2.5界面描述文件(mainwindow.ui

  • .ui 文件是用 Qt Designer 设计的界面布局文件。
  • 本质上是一个 XML 格式,描述了控件的摆放、信号槽连接等。
  • 在编译时,uic(User Interface Compiler) 会把 .ui 文件转化为对应的 C++ 代码。

你可以通过双击 .ui 文件,在可视化界面中拖拽控件,生成界面布局。

3.项目构建流程

在完成 .pro、源代码(.cpp.h)、界面文件(.ui)之后,Qt项目需要经过一系列构建流程,才能生成最终可以运行的可执行程序。 下面,我们从宏观角度和细节角度,一步步梳理整个流程。

3.1整体流程概述

Qt 项目的构建流程可以总结为:

rust 复制代码
.pro文件 -> 生成Makefile -> 代码编译 + ui编译 + moc编译 + rcc编译 -> 链接 -> 可执行文件

具体分为如下步骤:

阶段 主要内容 备注
1 .pro 文件生成 Makefile 使用 qmake
2 预处理 .ui 文件 使用 uic(User Interface Compiler)
3 处理带 Q_OBJECT 宏的类 使用 moc(Meta-Object Compiler)
4 处理 .qrc 资源文件 使用 rcc(Resource Compiler)
5 编译 .cpp 文件 调用 C++ 编译器(如 MSVC、g++ 等)
6 链接生成可执行文件 合并所有目标文件为一个程序

3.2详细步骤解析

在Qt Creater中打开我们的Qt项目,点击左侧【项目】,在这里其实就能够看到项目的构建步骤,其中说明就两步:

1.qmake:执行qmake.exe读取.pro文件生成Makefile

2.Make:执行mingw32-make.exe读取构建目录中Makefile进行编译,通过编译最终生成可执行文件。

回到项目,点击绿色运行按钮或按快捷键Ctrl+R运行项目,点击窗口下方的【编译输出】就能够看到类似这样的详细步骤:

ruby 复制代码
16:12:38: 为项目First_1执行步骤 ...
16:12:38: 正在启动 "D:\exploit\Qt\6.7.3\mingw_64\bin\qmake.exe" F:\Qt\2025_4\First_1\First_1.pro -spec win32-g++ "CONFIG+=debug" "CONFIG+=qml_debug"

Info: creating stash file F:\Qt\2025_4\First_1\build\Desktop_Qt_6_7_3_MinGW_64_bit-Debug\.qmake.stash
16:12:38: 进程"D:\exploit\Qt\6.7.3\mingw_64\bin\qmake.exe"正常退出。
16:12:38: 正在启动 "D:\exploit\Qt\Tools\mingw1120_64\bin\mingw32-make.exe" -f F:/Qt/2025_4/First_1/build/Desktop_Qt_6_7_3_MinGW_64_bit-Debug/Makefile qmake_all

mingw32-make: Nothing to be done for 'qmake_all'.
16:12:38: 进程"D:\exploit\Qt\Tools\mingw1120_64\bin\mingw32-make.exe"正常退出。
16:12:38: 正在启动 "D:\exploit\Qt\Tools\mingw1120_64\bin\mingw32-make.exe" -j16

D:/exploit/Qt/Tools/mingw1120_64/bin/mingw32-make -f Makefile.Debug
mingw32-make[1]: Entering directory 'F:/Qt/2025_4/First_1/build/Desktop_Qt_6_7_3_MinGW_64_bit-Debug'
D:\exploit\Qt\6.7.3\mingw_64\bin\uic.exe ..\..\mainwindow.ui -o ui_mainwindow.h
g++ -c -fno-keep-inline-dllexport -g -std=gnu++1z -Wall -Wextra -Wextra -fexceptions -mthreads -DUNICODE -D_UNICODE -DWIN32 -DMINGW_HAS_SECURE_API=1 -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_NEEDS_QMAIN -I../../../First_1 -I. -ID:/exploit/Qt/6.7.3/mingw_64/include -ID:/exploit/Qt/6.7.3/mingw_64/include/QtWidgets -ID:/exploit/Qt/6.7.3/mingw_64/include/QtGui -ID:/exploit/Qt/6.7.3/mingw_64/include/QtCore -Idebug -I. -I/include -ID:/exploit/Qt/6.7.3/mingw_64/mkspecs/win32-g++  -o debug\main.o ..\..\main.cpp
g++ -fno-keep-inline-dllexport -g -std=gnu++1z -Wall -Wextra -Wextra -dM -E -o debug\moc_predefs.h D:\exploit\Qt\6.7.3\mingw_64\mkspecs\features\data\dummy.cpp
g++ -c -fno-keep-inline-dllexport -g -std=gnu++1z -Wall -Wextra -Wextra -fexceptions -mthreads -DUNICODE -D_UNICODE -DWIN32 -DMINGW_HAS_SECURE_API=1 -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_NEEDS_QMAIN -I../../../First_1 -I. -ID:/exploit/Qt/6.7.3/mingw_64/include -ID:/exploit/Qt/6.7.3/mingw_64/include/QtWidgets -ID:/exploit/Qt/6.7.3/mingw_64/include/QtGui -ID:/exploit/Qt/6.7.3/mingw_64/include/QtCore -Idebug -I. -I/include -ID:/exploit/Qt/6.7.3/mingw_64/mkspecs/win32-g++  -o debug\mainwindow.o ..\..\mainwindow.cpp
D:\exploit\Qt\6.7.3\mingw_64\bin\moc.exe -DUNICODE -D_UNICODE -DWIN32 -DMINGW_HAS_SECURE_API=1 -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_NEEDS_QMAIN --include F:/Qt/2025_4/First_1/build/Desktop_Qt_6_7_3_MinGW_64_bit-Debug/debug/moc_predefs.h -ID:/exploit/Qt/6.7.3/mingw_64/mkspecs/win32-g++ -IF:/Qt/2025_4/First_1 -ID:/exploit/Qt/6.7.3/mingw_64/include -ID:/exploit/Qt/6.7.3/mingw_64/include/QtWidgets -ID:/exploit/Qt/6.7.3/mingw_64/include/QtGui -ID:/exploit/Qt/6.7.3/mingw_64/include/QtCore -I. -ID:/exploit/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++ -ID:/exploit/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/x86_64-w64-mingw32 -ID:/exploit/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/backward -ID:/exploit/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include -ID:/exploit/Qt/Tools/mingw1120_64/lib/gcc/x86_64-w64-mingw32/11.2.0/include-fixed -ID:/exploit/Qt/Tools/mingw1120_64/x86_64-w64-mingw32/include ..\..\mainwindow.h -o debug\moc_mainwindow.cpp
g++ -c -fno-keep-inline-dllexport -g -std=gnu++1z -Wall -Wextra -Wextra -fexceptions -mthreads -DUNICODE -D_UNICODE -DWIN32 -DMINGW_HAS_SECURE_API=1 -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_NEEDS_QMAIN -I../../../First_1 -I. -ID:/exploit/Qt/6.7.3/mingw_64/include -ID:/exploit/Qt/6.7.3/mingw_64/include/QtWidgets -ID:/exploit/Qt/6.7.3/mingw_64/include/QtGui -ID:/exploit/Qt/6.7.3/mingw_64/include/QtCore -Idebug -I. -I/include -ID:/exploit/Qt/6.7.3/mingw_64/mkspecs/win32-g++  -o debug\moc_mainwindow.o debug\moc_mainwindow.cpp
g++ -Wl,-subsystem,windows -mthreads -o debug\First_1.exe debug/main.o debug/mainwindow.o debug/moc_mainwindow.o D:\exploit\Qt\6.7.3\mingw_64\lib\libQt6Widgets.a D:\exploit\Qt\6.7.3\mingw_64\lib\libQt6Gui.a D:\exploit\Qt\6.7.3\mingw_64\lib\libQt6Core.a -lmingw32 D:\exploit\Qt\6.7.3\mingw_64\lib\libQt6EntryPoint.a -lshell32  
mingw32-make[1]: Leaving directory 'F:/Qt/2025_4/First_1/build/Desktop_Qt_6_7_3_MinGW_64_bit-Debug'
16:12:46: 进程"D:\exploit\Qt\Tools\mingw1120_64\bin\mingw32-make.exe"正常退出。
16:12:46: Elapsed time: 00:08.

内容很多,一点点讲解。

1.运行qmake生成Makefile

日志:

arduino 复制代码
正在启动 "D:\exploit\Qt\6.7.3\mingw_64\bin\qmake.exe" F:\Qt\2025_4\First_1\First_1.pro -spec win32-g++ "CONFIG+=debug" "CONFIG+=qml_debug"

解释:

  • 执行qmake.exe 程序读取你的 .pro 文件,根据里面的配置(源文件、头文件、UI文件、编译选项等),生成一份 Makefile
  • 参数 -spec win32-g++ 告诉 qmake 使用 MinGW 的编译规则(而不是MSVC等)。
  • CONFIG+=debug 是生成调试版(会带调试信息、不开启优化,适合开发阶段)。
  • CONFIG+=qml_debug 是为了支持 QML 调试(你的项目如果用到了 QML,就能远程调试了)。
  • 生成中途,创建了一个 .qmake.stash 文件:缓存一些变量,加快下次构建速度。

2.运行 mingw32-make qmake_all (检查 Makefile)

日志:

arduino 复制代码
正在启动 "mingw32-make.exe" -f Makefile qmake_all

输出:

vbnet 复制代码
mingw32-make: Nothing to be done for 'qmake_all'.

解释:

  • qmake_all 是一个Makefile里的伪目标,意思是"如果有文件变化,重新跑qmake"。
  • 这里输出 "Nothing to be done" ,说明 文件没有变化,不需要重新生成 Makefile。

3.开始实际编译:mingw32-make -j16

日志:

arduino 复制代码
正在启动 "mingw32-make.exe" -j16

解释:

  • -j16 表示用 16个线程并行加速编译(你的机器应该有多核CPU)。

之后就是实际的编译和链接过程了,细分成几步:

4.Qt工具链生成辅助代码(uicmoc

(1)uic处理UI文件

日志:

复制代码
uic.exe mainwindow.ui -o ui_mainwindow.h

解释:

  • uic (User Interface Compiler) 把 .ui 文件(Qt Designer设计的窗口)转换成对应的 C++ 头文件 ui_mainwindow.h
  • 这个头文件里就是窗口里的控件定义,比如按钮、文本框等。
(2)编译源文件

日志:

css 复制代码
g++ -c ... -o debug\main.o ..\..\main.cpp

解释:

-c:只编译不链接。

main.cpp 编译为 main.o(目标文件)。

(3)预处理宏定义文件

日志:

arduino 复制代码
g++ -fno-keep-inline-dllexport -g ... -dM -E -o debug\moc_predefs.h dummy.cpp

解释:

  • 收集当前编译环境的宏定义,比如系统平台、编译器特性,供后续的 moc 使用。
  • -dM -E:只做预处理并输出宏定义,用来辅助 moc
  • moc_predefs.h 是生成 moc 代码时需要包含的宏环境。
(4)moc处理信号与槽

日志:

复制代码
moc.exe mainwindow.h -o moc_mainwindow.cpp

解释:

  • moc (Meta-Object Compiler) 处理你的 .h 文件,分析类中定义的 signalsslotsQ_OBJECT 宏。
  • 生成 moc_mainwindow.cpp,它里面是 Qt 的信号槽机制所需要的底层代码。

5.编译源代码

(1)编译你的.cpp文件

日志:

css 复制代码
g++ -c main.cpp -o debug\main.o
g++ -c mainwindow.cpp -o debug\mainwindow.o

解释:

  • -c 表示只编译成目标文件 .o,不会链接成可执行文件。
  • 每个 .cpp.o,独立编译。
  • 加了很多 -I 参数,是告诉编译器哪些目录里有头文件。
(2)编译辅助生成.cpp文件

日志:

lua 复制代码
g++ -c moc_mainwindow.cpp -o debug\moc_mainwindow.o

解释:

  • moc 生成的代码也要编译成 .o 文件,才能后面一起链接。

6.链接生成最终的.exe

日志:

lua 复制代码
g++ -Wl,-subsystem,windows -mthreads -o debug\First_1.exe ...

解释:

链接(Link)阶段 ,把所有 .o 文件合并起来,生成最终可执行文件 First_1.exe

链接了必要的 Qt库,比如:

  • libQt6Widgets.a
  • libQt6Gui.a
  • libQt6Core.a
  • libQt6EntryPoint.a

还有系统库,比如 -lshell32

-Wl,-subsystem,windows 参数告诉 Windows,这是个 窗口程序(没有控制台窗口弹出)。

7.构建完成,退出

日志:

lua 复制代码
进程"mingw32-make.exe"正常退出。
Elapsed time: 00:08

解释:

  • 构建过程没有错误,成功生成了 debug\First_1.exe
  • 整个过程用了 8秒钟,效率还可以。

3.3 构建流程图

graph TD qmake解析.pro文件 --> 生成Makefile-->uic处理.ui文件,生成ui_xxx.h-->g++编译main.cpp生成main.o\ng++编译mainwindow.cpp生成mainwidow.o-->g++预处理dummy.cpp生成moc_predefs.h-->moc处理mainwindow.h生成moc_mainwindow.cpp-->g++编译moc_mainwindow.cpp生成moc_mainwindow.o-->g++链接所有.o文件生成.exe程序

4.总结

本文详细分析了 Qt 项目在构建过程中的各个步骤,包括 qmake 生成 Makefile、uic 处理 UI 文件、moc 处理头文件、g++ 编译与链接过程。通过一步步解读实际编译日志,理清了 Qt 工程背后复杂但有序的编译流程,并绘制了对应的流程图,方便理解和记忆。

相关推荐
雾削木8 分钟前
mAh 与 Wh:电量单位的深度解析
开发语言·c++·单片机·嵌入式硬件·算法·电脑
我真的不会C2 小时前
QT中的事件及其属性
开发语言·qt
工藤新一¹3 小时前
C++/SDL进阶游戏开发 —— 双人塔防游戏(代号:村庄保卫战 13)
c++·游戏·游戏引擎·毕业设计·sdl·c++游戏开发·渲染库
让我们一起加油好吗3 小时前
【C++】类和对象(上)
开发语言·c++·visualstudio·面向对象
好想有猫猫3 小时前
【Redis】服务端高并发分布式结构演进之路
数据库·c++·redis·分布式·缓存
不是杠杠3 小时前
驼峰命名法(Camel Case)与匈牙利命名法(Hungarian Notation)详解
c++
Epiphany.5563 小时前
基于c++的LCA倍增法实现
c++·算法·深度优先
落羽的落羽3 小时前
【落羽的落羽 C++】vector
c++
newki4 小时前
学习笔记,Linux虚拟机中C/C++的编译相关流程步骤
c语言·c++