文章

使用命令行编译cpp

使用命令行编译cpp

[TOC]

以下是通过命令行编译一个简单 cpp(例如1中的 cmd.cpp) 的方法:

1. vs命令行 + cl 编译

cl 编译器:2

MSVC(Microsoft Visual C++)是微软提供的C++编译器,cl 是MSVC编译器的命令行界面工具,是 Microsoft Visual Studio 集成开发环境的一部分。专门用于编译和链接 Microsoft Windows 平台上的 C 和 C++ 程序。支持生成 32 位和 64 位程序,并兼容 C99 和 C++ 标准的多个版本,以及部分 C11 和 C++17/20 的特性。

打开一个 VS 的命令提示行窗口(若不是 VS 的命令行窗口的话会在编译的时候提示 “找不到 cl” 之类的报错)。

将目录切换到对应的盘符,因为 VS 在 E 盘,而 cpp 所在的路径在 D 盘:

1
D:

将路径切换到 cpp 所在路径:

1
cd D:\DeskTop\testGCC

编译 cpp ,就会在同级目录下产生一个同名的 cmd.obj 文件:

1
cl /c cmd.cpp

链接 obj 文件,就会在同级目录下产生 cmd.dll、cmd.lib 和 cmd.exp 三个文件,MyFunc是 cpp 中的导出函数:

1
link cmd.obj /DLL /NOENTRY /EXPORT:MyFunc

2. g++ 编译

(1) g++ 编译器介绍

可以通过 vscode 或 windows 自带的命令行使用 g++ 编译器编译该 cpp。

g++ 编译器:2

g++ 是 GNU 编译器集合(GNU Compiler Collection,简称 GCC)中的 C++ 编译器。它是一个自由软件,支持多种编程语言,g++ 特指其用于编译C++程序的部分。GCC 是在 GNU 项目下开发的,旨在提供一个完整的、跨平台的、自由的编程语言编译器,特别是为了支持自由软件的开发。

以下是关于 g++ 的一些关键特性:

  1. 跨平台支持g++ 可以在多种架构和操作系统上运行,包括但不限于 Linux、macOS、Windows(通常通过 Cygwin 或 MinGW)、以及各种 UNIX 版本。
  2. 语言标准g++ 支持多种C++标准,包括C++98、C++03、C++11、C++14、C++17、甚至C++20的许多特性。可以通过命令行参数来指定编译时要遵循的具体标准,例如使用 -std=c++11 来遵循 C++11 标准。
  3. 优化g++ 提供了多级别的优化选项,从 -O0(无优化)到 -O3(开启大多数优化)以及 -Ofast(忽略严格标准兼容性,开启所有优化)。此外,还有针对特定用例的优化选项,比如 -Os(针对空间优化,使得生成的可执行文件尽可能小)。
  4. 调试支持g++ 可以与 GNU 调试器(GDB)无缝集成。通过 -g 标志可以在编译时生成调试信息,使得开发者能够进行源码级调试。
  5. 扩展特性:虽然 g++ 支持标准C++,它还提供了一些扩展特性,这些特性并不是C++标准的一部分,但在某些情况下可能会很有用。然而,依赖于这些扩展可能会影响代码的移植性。
  6. 错误和警告g++ 在编译时可以生成详细的错误和警告信息,这对于调试代码非常有帮助。通过 -Wall-Wextra 等标志可以开启额外的警告信息,有助于识别潜在问题。
  7. 内联汇编g++ 支持在C++代码中嵌入汇编语言,这对于需要手动优化以达到硬件特定性能的高级编程任务来说非常有用。

如需使用 g++ 编译目标 cpp ,需要在电脑上安装 g++

g++ 编译器版本:2

  1. MinGW(Minimalist GNU for Windows): MinGW提供了一套完整的开源编程工具集合,包括 g++。它是将GCC编译器和相关工具(如GDB)移植到Windows的项目。
  2. MinGW-w64: MinGW-w64是MinGW的一个分支,支持64位Windows,也为32位Windows提供支持,并且包含了 g++
  3. Cygwin: Cygwin提供了一个在Windows上运行的类UNIX环境,它包含了 g++ 和许多其他GNU工具。Cygwin更像是一个模拟层,使得很多在UNIX和Linux上的工具能够在Windows上运行。
  4. MSYS2: MSYS2是基于MinGW-w64的软件分发和构建平台,提供了一个包管理系统和bash shell,旨在提供一个便利的开发环境。MSYS2中也包含了 g++
  5. Windows Subsystem for Linux (WSL): WSL允许你在Windows 10和Windows 11上运行Linux环境,包括大多数命令行工具、实用程序和应用程序,直接在Windows上,而无需复杂的配置。安装了WSL后,你可以使用Linux发行版的软件包管理器(如apt-get在Ubuntu上)安装 g++
  6. 第三方工具如 TDM-GCC: TDM-GCC是MinGW的一个分支,它专注于提供用于Windows的GCC编译器,包括 g++,并且它提供了易于安装的安装程序。

(2) 使用vscode的命令行编译

使用 vscode 打开文件目录,将目录切换到 cpp 所在的文件夹,Ctrl + Shift + ` 打开终端,在终端中输入以下命令,把一个名为 cmd.cpp 的文件编译为 mydll.dll:

1
g++ -pthread -shared -Os -s -o mydll.dll cmd.cpp -std=c++17 -fPIC

下面是这个命令中各个选项的意义:

  • g++:这是GCC中用于编译C++程序的编译器。
  • -pthread :链接 POSIX 线程库。这会告诉编译器在链接时包含所需的线程支持。(20240217-HSB:利用“打包”项目实际测试的时候有无此项时间是一样的)
  • -shared:这个选项指示编译器生成一个共享对象文件(在Windows上是DLL)。
  • -Os:指示编译器进行优化,旨在使生成的代码占用空间尽可能小,同时不会过分影响执行速度。
  • -s :剥离调试信息。为了能够调试,需要将-s改为-g-g代表生成调试信息。
  • -o mydll.dll:这个选项指定了输出文件的名字。在这种情况下,输出文件是mydll.dll
  • cmd.cpp:这是待编译的源文件。
  • -std=c++17 :启用 C++17 标准。
  • -fPIC :用于GCC编译器的编译选项,它表示生成“位置无关代码”(Position-Independent Code)。

若项目中不止一个cpp,就在命令后面把需要的cpp都加上:

1
g++ -shared -Os -o mydll.dll cmd.cpp need.cpp -fPIC

为了能够调试,需要将-s改为-g,这个选项会告诉编译器生成调试信息;Os改为O0表示不再优化:

1
g++ -shared -O0 -g -o mydll.dll cmd.cpp

若定义了main函数的话,也可以编译为exe

1
g++ -O0 -g main.cpp -o myExe.exe

(3) 使用cmd命令行编译

Win + R 然后输入 cmd 启动 Windows 自带的命令行窗口,将运行目录切换到 cpp 所在目录,然后输入上面的命令,同样也可以把该 cmd.cpp 编译为 mydll.dll。

稍等片刻就会在同目录下输出一个名为 mydll.dll 的动态库。

3. 一些问题

(1)两种编译产生的dll大小不同

按照如上操作,都可以编译出所需的 dll,也均能在另一个程序中被动态加载并调用接口,但是在 vscode 中编译出的 dll 为 970KB,而在 vs 命令行中编译出的 dll 仅为 2KB。

可能是因为如下原因导致:2

  1. 默认编译选项:g++和cl可能会使用不同的默认编译选项。这些选项可以影响代码生成和优化策略,从而可能导致生成的DLL大小不同。
  2. 优化级别:不同的编译器可能对优化级别进行不同的处理。默认情况下,g++会启用一些优化选项,例如-O1、-O2或-O3,而cl编译器可能会使用不同的优化级别。这些优化级别的差异会导致生成的代码大小不同。
  3. 链接方式:编译器在链接代码时采用的链接方式可能会有所不同。不同的链接方式可能会导致生成的DLL大小有所差异。
  4. 默认标准库:g++和cl可能使用不同的默认标准库。标准库的大小和实现方式会影响最终生成的DLL大小。
  5. 编译器版本:不同版本的编译器可能对代码生成和优化有所改进。因此,不同版本的g++和cl可能会导致生成的DLL大小不同。

(2)编译产生的文件不同

采用 g++ 编译时,仅会有一个 dll,而采用 cl 编译时,还会同时产生 .lib.exp 文件,原因可能如下:2

在Windows平台上,使用g++编译器生成的动态链接库(DLL)通常不会自动生成对应的.lib和.exp文件。这是因为g++编译器使用了不同的导出符号和符号导出表的约定。

在Windows上,使用Visual C++(cl编译器)编译器生成的DLL会自动生成对应的.lib和.exp文件。.lib文件是用来进行静态链接的库文件,包含了DLL中导出的函数和符号的信息。.exp文件是导出符号表文件,包含了DLL中导出函数和符号的列表。这些文件在链接时用于解析和匹配符号引用。

相反,在g++编译器中,通常会使用其他方式来处理导出符号和符号导出表的约定,如在代码中使用 __declspec(dllexport) __attribute__((visibility("default"))) 来显式声明导出符号。这种方式不会生成独立的.lib和.exp文件,而是将导出符号信息直接嵌入到生成的DLL文件中。

需要注意的是,对于g++编译器生成的DLL,如果你需要与其他工具或编译器进行链接,可能需要手动创建相应的.lib文件或提供其他方式的导出符号信息。

总结来说,g++和cl编译器在处理DLL导出符号和符号导出表的方式上存在差异,导致了在Windows平台上,采用g++编译生成的DLL通常不会自动生成对应的.lib和.exp文件。

参考文章34

本文由作者按照 CC BY 4.0 进行授权