游戏引擎学习第16天
视频参考:https://www.bilibili.com/video/BV1mEUCY8EiC/
这些字幕讨论了编译器警告的概念以及如何在编译过程中启用和处理警告。以下是字幕的内容摘要:
-
警告的定义:警告是编译器用来告诉你某些地方可能存在问题,尽管编译器不强制要求你修复它们。警告并不会阻止编译过程,但它们通常提示代码中可能存在潜在问题。
-
警告级别:警告通常分为不同的级别,例如1级、2级、3级、4级等。你可以选择启用一个警告级别,以便只关注特定类型的警告,而不是每一个单独的警告。
-
启用警告:作者提到通过启用较高级别的警告,可以让编译器显示更多的警告信息,帮助开发者发现潜在问题。
-
解决警告:一些警告是可以修复的,而其他警告则可能是由于外部库或系统文件(如
windows.h)引起的,在这些情况下可能无法轻易修复。 -
警告级别的调整:如果某些警告级别的警告过于繁琐,可以通过降低警告级别来减少警告的数量,从而使得编译过程更加顺利。
-
具体示例:提到了一些警告信息,例如编译器告诉开发者结构体中可能插入了填充,或者警告与 Windows 头文件相关的问题。
总结起来,这段对话讨论了编译器警告的启用、调整和处理策略,帮助开发者理解如何更有效地管理编译时的警告信息。
在MSVC(Microsoft Visual C++)中,警告级别用于控制编译器输出的警告信息的详细程度。MSVC提供了四个警告级别,分别为:
- 级别 0 (
/W0):关闭所有警告。适用于不希望看到任何警告的情况,但不建议在开发中使用,因为可能会错过重要的提示。 - 级别 1 (
/W1):显示最低级别的警告。适用于仅查看最严重警告的情况。 - 级别 2 (
/W2):显示常见的警告信息,通常是开发过程中需要注意的警告。 - 级别 3 (
/W3):显示更多警告信息,是默认的警告级别。适用于大多数开发场景。 - 级别 4 (
/W4):显示所有警告,包括那些可能无关紧要的。适用于希望尽可能详细地查看警告信息的情况。 - 级别 5 (
/Wall):显示所有警告,包括非常详细的信息。用于调试时极为详尽的检查。
cmake 中添加警告参数
# 为 MSVC 编译器添加参数
if(MSVC) # 如果编译器是 MSVC(Microsoft Visual C++)# 设置 C++ 编译选项set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX /W4 /wd4819")# /WX : 将所有警告视为错误(会让编译因警告失败)# /W4 : 设置警告级别为 4,显示大多数警告# /wd4819 : 屏蔽警告 C4819,避免文件编码问题导致的警告
endif()
下面是解决相关的警告
C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp
[build] cl: 命令行 warning D9025 :正在重写“/W3”(用“/W4”)
[build] C:\Users\16956\Documents\game\day16\game\game\game.h(134): error C2220: 以下警告被视为错误
[build] C:\Users\16956\Documents\game\day16\game\game\game.h(134): warning C4201: 使用了非标准扩展: 无名称的结构/联合
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(110): warning C4244: “参数”: 从“uint64”转换到“DWORD”,可能丢失数据
[build] C:\Users\16956\Documents\game\day16\game\game\game.cpp(27): warning C4244: “初始化”: 从“int”转换到“uint8”,可能丢失数据
[build] C:\Users\16956\Documents\game\day16\game\game\game.cpp(28): warning C4244: “初始化”: 从“int”转换到“uint8”,可能丢失数据
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(161): warning C4100: “pState”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(161): warning C4100: “dwUserIndex”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(167): warning C4100: “pVibration”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(167): warning C4100: “dwUserIndex”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(383): warning C4100: “Height”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(383): warning C4100: “Width”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(383): warning C4100: “Y”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(382): warning C4100: “X”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(573): warning C4100: “cmdshow”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(573): warning C4100: “cmdline”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(572): warning C4100: “hInstPrev”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(619): warning C4189: “xOffset”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(620): warning C4189: “yOffset”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(878): warning C4189: “MillisecondPerFrame”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(892): warning C4189: “Temp”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(881): warning C4189: “FPS”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(882): warning C4189: “MCPF”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(725): warning C4189: “Left”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(724): warning C4189: “Down”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(726): warning C4189: “Right”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(723): warning C4189: “Up”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\game.cpp(35): warning C4505: “GameStartup”: 已删除具有内部链接的未引用函数
[build] C:\Users\16956\Documents\game\day16\game\game\game.cpp(45): warning C4505: “GameShutDown”: 已删除具有内部链接的未引用函数
[build] ninja: build stopped: subcommand failed.
[build] C:\Users\16956\Documents\game\day16\game\game\game.h(134): warning C4201: 使用了非标准扩展: 无名称的结构/联合
warning C4201 是 MSVC 中的一个警告,它提示你使用了非标准扩展:无名称的结构或联合。这意味着在你的代码中,你定义了一个没有名称的匿名结构体或联合体。
IDE 显示的是一个插件

解决上面的警告

问题出现在 union 内部的结构体部分:
union {game_button_state Button[6]; // 按钮状态(最多 6 个按钮)struct {game_button_state Up; // 上方向键状态game_button_state Down; // 下方向键状态game_button_state Left; // 左方向键状态game_button_state Right; // 右方向键状态game_button_state LeftShoulder; // 左肩键状态game_button_state RightShoulder; // 右肩键状态};
};
这里,struct 是一个匿名结构体,没有给它命名。因此,这会触发 warning C4201,因为这种匿名结构体的使用违反了标准的 C++ 规则,虽然 MSVC 允许它作为扩展。
如何理解这个警告
C++ 标准要求结构体或联合体应当具备一个名字,便于在后续的代码中引用。匿名结构体或联合体虽然在某些编译器中被允许,但它们并不符合标准,因此会触发 C4201 警告。
如何解决
要解决这个问题,你需要给匿名的结构体命名。可以通过以下方式修改你的代码:
union {game_button_state Button[6]; // 按钮状态(最多 6 个按钮)struct game_controller_buttons {game_button_state Up; // 上方向键状态game_button_state Down; // 下方向键状态game_button_state Left; // 左方向键状态game_button_state Right; // 右方向键状态game_button_state LeftShoulder; // 左肩键状态game_button_state RightShoulder; // 右肩键状态};
};
在上面的代码中,game_controller_buttons 是给结构体命名的名称,这样就不会触发 C4201 警告了。
总结
warning C4201 提示你在代码中使用了匿名结构体或联合体,这可能是编译器的扩展功能,但不符合 C++ 标准。通过为结构体或联合体命名,可以解决这个警告。
这个警告我们不认为是一个错误
set(CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} /WX /W4 /wd4819 /wdc4201”)
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(110): warning C4244: “参数”: 从“uint64”转换到“DWORD”,可能丢失数据

[build] C:\Users\16956\Documents\game\day16\game\game\game.cpp(27): warning C4244: “初始化”: 从“int”转换到“uint8”,可能丢失数据
[build] C:\Users\16956\Documents\game\day16\game\game\game.cpp(28): warning C4244: “初始化”: 从“int”转换到“uint8”,可能丢失数据

[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(161): warning C4100: “pState”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(161): warning C4100: “dwUserIndex”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(167): warning C4100: “pVibration”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(167): warning C4100: “dwUserIndex”: 未引用的形参

warning C4100 是 MSVC 的一个警告,表明某个函数参数未被使用。在你的代码中,这些未使用的参数是 pState、pVibration 和 dwUserIndex,它们出现在以下打桩函数中:
X_INPUT_GET_STATE(XInputGetStateStub) { return (ERROR_DEVICE_NOT_CONNECTED);
}X_INPUT_SET_STATE(XInputSetStateStub) { return (ERROR_DEVICE_NOT_CONNECTED);
}
由于打桩函数的实现仅返回 ERROR_DEVICE_NOT_CONNECTED,而没有使用这些参数,因此触发了 C4100 警告。
解决方法
有几种方法可以消除这个警告:
方法 1:显式地标记未使用的参数
可以通过显式引用参数来避免警告,即使没有实际使用它们:
X_INPUT_GET_STATE(XInputGetStateStub) { (void)dwUserIndex; // 明确声明未使用(void)pState; // 明确声明未使用return (ERROR_DEVICE_NOT_CONNECTED);
}X_INPUT_SET_STATE(XInputSetStateStub) { (void)dwUserIndex; // 明确声明未使用(void)pVibration; // 明确声明未使用return (ERROR_DEVICE_NOT_CONNECTED);
}
这种方式简单直接,表明你有意不使用这些参数。
方法 2:使用 MSVC 特定的宏
MSVC 提供了一个 UNREFERENCED_PARAMETER 宏,可以标记未使用的参数。它会自动消除警告:
#include <windows.h> // 包含 UNREFERENCED_PARAMETER 宏X_INPUT_GET_STATE(XInputGetStateStub) { UNREFERENCED_PARAMETER(dwUserIndex);UNREFERENCED_PARAMETER(pState);return (ERROR_DEVICE_NOT_CONNECTED);
}X_INPUT_SET_STATE(XInputSetStateStub) { UNREFERENCED_PARAMETER(dwUserIndex);UNREFERENCED_PARAMETER(pVibration);return (ERROR_DEVICE_NOT_CONNECTED);
}
方法 3:修改函数签名
如果这些参数完全无用(仅用于符合接口定义),可以使用未命名的参数形式:
X_INPUT_GET_STATE(XInputGetStateStub) { return (ERROR_DEVICE_NOT_CONNECTED);
}X_INPUT_SET_STATE(XInputSetStateStub) { return (ERROR_DEVICE_NOT_CONNECTED);
}
然而,如果这些参数在其他上下文中需要使用,不推荐这种方式。
方法 4:全局屏蔽警告
如果这些警告在项目中普遍存在,也可以在编译器选项或代码中禁用 C4100 警告:
-
CMake 中添加编译选项:
if(MSVC)add_compile_options(/wd4100) # 禁用 C4100 警告 endif() -
在代码中使用
#pragma:#pragma warning(push) #pragma warning(disable: 4100)X_INPUT_GET_STATE(XInputGetStateStub) { return (ERROR_DEVICE_NOT_CONNECTED); }X_INPUT_SET_STATE(XInputSetStateStub) { return (ERROR_DEVICE_NOT_CONNECTED); }#pragma warning(pop)
总结
推荐优先使用 方法 1 或 方法 2,显式标记未使用的参数,既解决了警告,也保留了代码的可读性。如果打桩函数广泛使用且参数确实无用,可以考虑 方法 4 全局屏蔽。
c++ 属性
在 C++ 中,除了使用编译器特定的宏(如 MSVC 的 UNREFERENCED_PARAMETER),还可以利用标准或扩展的函数属性来表明某个参数未被使用,从而避免警告。以下是常见的实现方式:
1. 使用 C++17 的 [[maybe_unused]] 属性
从 C++17 开始,标准引入了 [[maybe_unused]] 属性,用于标记未被使用的变量或参数。这样可以告诉编译器,此参数未被使用是有意的,不需要警告。
X_INPUT_GET_STATE(XInputGetStateStub) {[[maybe_unused]] DWORD dwUserIndex; // 参数标记为未使用[[maybe_unused]] XINPUT_STATE* pState;return (ERROR_DEVICE_NOT_CONNECTED);
}
也可以直接在参数列表中使用:
X_INPUT_GET_STATE(XInputGetStateStub([[maybe_unused]] DWORD dwUserIndex, [[maybe_unused]] XINPUT_STATE* pState)) {return (ERROR_DEVICE_NOT_CONNECTED);
}
- 优点:标准化,跨编译器支持。
- 缺点:需要使用 C++17 或更新版本。
2. 使用编译器特定的属性
不同编译器提供了自己的方式来标记未使用的参数:
(a) GCC 和 Clang: __attribute__((unused))
适用于 GCC 和 Clang,可以通过 __attribute__((unused)) 标记参数:
X_INPUT_GET_STATE(XInputGetStateStub(DWORD __attribute__((unused)) dwUserIndex, XINPUT_STATE* __attribute__((unused)) pState)) {return (ERROR_DEVICE_NOT_CONNECTED);
}
或者用在局部变量上:
X_INPUT_GET_STATE(XInputGetStateStub) {DWORD dwUserIndex __attribute__((unused));XINPUT_STATE* pState __attribute__((unused));return (ERROR_DEVICE_NOT_CONNECTED);
}
(b) MSVC: __pragma(warning(suppress: 4100))
MSVC 提供了特定的 __pragma 语法来抑制特定警告:
X_INPUT_GET_STATE(XInputGetStateStub) { __pragma(warning(suppress: 4100)) DWORD dwUserIndex;__pragma(warning(suppress: 4100)) XINPUT_STATE* pState;return (ERROR_DEVICE_NOT_CONNECTED);
}
3. 使用通用技巧
如果无法使用上述属性,可以使用一些编程技巧,告诉编译器这是有意未使用的参数:
(a) 强制类型转换
通过将参数强制转换为 void,可以消除未使用参数的警告:
X_INPUT_GET_STATE(XInputGetStateStub) {(void)dwUserIndex; // 明确声明未使用(void)pState; // 明确声明未使用return (ERROR_DEVICE_NOT_CONNECTED);
}
(b) 使用 [[nodiscard]] 配合
对于返回值函数,可以结合 [[nodiscard]] 属性强调参数未被使用,但函数仍需要处理。
总结
- 如果项目使用 C++17 或更新版本,优先使用
[[maybe_unused]],这是标准化的方式。 - 如果需要支持 旧版本编译器:
- GCC/Clang:使用
__attribute__((unused))。 - MSVC:使用
__pragma或UNREFERENCED_PARAMETER宏。
- GCC/Clang:使用
- 若无需依赖编译器特性,可使用
(void)转换法,这是一种跨平台兼容的解决方案。
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(383): warning C4100: “Height”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(383): warning C4100: “Width”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(383): warning C4100: “Y”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(382): warning C4100: “X”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(573): warning C4100: “cmdshow”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(573): warning C4100: “cmdline”: 未引用的形参
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(572): warning C4100: “hInstPrev”: 未引用的形参


[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(619): warning C4189: “xOffset”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(620): warning C4189: “yOffset”: 局部变量已初始化但不引用

[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(878): warning C4189: “MillisecondPerFrame”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(892): warning C4189: “Temp”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(881): warning C4189: “FPS”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(882): warning C4189: “MCPF”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(725): warning C4189: “Left”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(724): warning C4189: “Down”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(726): warning C4189: “Right”: 局部变量已初始化但不引用
[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(723): warning C4189: “Up”: 局部变量已初始化但不引用

[build] C:\Users\16956\Documents\game\day16\game\game\game.cpp(35): warning C4505: “GameStartup”: 已删除具有内部链接的未引用函数
[build] C:\Users\16956\Documents\game\day16\game\game\game.cpp(45): warning C4505: “GameShutDown”: 已删除具有内部链接的未引用函数

[build] C:\Users\16956\Documents\game\day16\game\game\win32_game.cpp(844) : warning C4701: 使用了可能未初始化的局部变量“ByteToLock”

14:10 常用使用的其他命令行选项
在 Visual Studio 中,-FC 和 -Zi 是用于编译器的两个选项,分别用于控制文件路径的显示和调试信息的生成。
-FC 参数
全称:Full Path to Source Code Files
- 作用:启用此选项后,编译器在生成的调试信息中会记录源代码文件的完整路径,而不仅仅是文件名。
- 用途:
- 在调试或错误日志中,完整路径有助于区分同名但位于不同目录的文件。
- 对于大型项目或涉及多层文件夹的项目,使用
-FC可以减少文件路径冲突引起的歧义。
-Zi 参数
全称:Generate Program Database for Debugging
- 作用:启用此选项后,编译器会生成包含详细调试信息的 PDB(Program Database)文件。
- 用途:
- 使调试工具(如 Visual Studio 调试器)能够通过 PDB 文件查看代码中的变量、函数、调用堆栈等信息。
- 是调试模式下的默认配置,用于支持断点调试、单步执行和查看源代码。
综合使用
-FC 通常与 -Zi 一起使用,尤其在调试场景中:
- 完整路径(
-FC)有助于清楚地定位源代码文件。 - 调试信息(
-Zi)提供详细的调试能力。
示例
如果你在 VS 中手动编译代码,可以添加如下命令:
cl -Zi -FC source.cpp
这将生成包含完整路径的调试信息文件(.pdb 文件)。
在 Visual Studio 的编译器中,-Oi 是一个优化选项,用于启用 内联函数调用,提高程序的性能。
作用
-Oi 参数启用编译器对特定内建函数(intrinsic functions)的内联替换。
- 内建函数(Intrinsic Functions):是编译器提供的一些特殊函数,通常映射到特定的 CPU 指令。例如,
memcpy、strcmp或数学函数如sqrt。 - 启用
-Oi后,这些函数在编译时被替换为等效的高效汇编指令,而不是通过函数调用完成任务。
优点
- 性能提升:
- 减少函数调用的开销(如栈操作和返回值处理)。
- 提高执行效率,因为直接使用了 CPU 指令。
- 优化代码大小:
- 内联代码有时能减少代码膨胀,尤其是对于小函数。
- 使用高级指令:
- 编译器可能使用更高效的 CPU 指令来实现特定功能。
可能的缺点
- 代码大小增加:
- 如果过多的函数被内联,可能导致生成的代码体积增大。
- 兼容性问题:
- 某些内建函数可能与特定硬件架构相关,因此需要确保目标硬件支持。
- 调试复杂性:
- 内联后的代码可能难以逐步调试,因为它不再是显式的函数调用。
使用场景
- 适用:高性能应用,尤其是对数学计算、字符串操作等性能敏感的场景。
- 不适用:代码大小有限制或者需要清晰的调试信息时。
相关指令
-Oi 通常与其他优化选项结合使用:
-O2:最大化优化性能(包括-Oi)。-Ox:极致优化(也启用-Oi)。-Ob:控制内联的函数数量。
示例
编译命令:
cl -Oi source.cpp
代码示例:
编译器可能将以下代码:
#include <math.h>double square_root(double x) {return sqrt(x);
}
转为内联的汇编指令,而不是调用标准库函数 sqrt。
总结:使用 -Oi 能提升性能,但需根据项目需求权衡性能与代码体积之间的关系。
在 Visual Studio 编译器中,-GR- 和 -EHa- 是控制运行时功能和异常处理机制的选项,它们的作用如下:
-GR- 参数
作用:禁用 RTTI(Run-Time Type Information)。
RTTI 简介
- RTTI 是 C++ 提供的一种运行时机制,允许程序在运行时动态查询对象的类型信息(例如使用
typeid和dynamic_cast)。 - 启用 RTTI(默认设置,
-GR)会在程序中生成额外的类型信息。
禁用 RTTI 的效果
- 减少代码大小:避免生成额外的类型信息,可以降低可执行文件的体积。
- 提升性能:避免运行时类型检查带来的额外开销。
- 限制功能:
typeid和dynamic_cast无法使用,程序中尝试调用它们会导致编译错误。
使用场景
- 适用于不依赖 RTTI 的代码,尤其是性能和代码大小敏感的项目(例如嵌入式开发)。
- 代码完全避免了多态和运行时类型查询。
-EHa- 参数
作用:禁用对结构化异常处理(SEH)和 C++ 异常的支持。
SEH 和 C++ 异常简介
- SEH:Windows 特有的异常机制,捕获系统级异常(如访问冲突)。
- C++ 异常:C++ 标准支持的异常机制(
try-catch块)。
禁用异常支持的效果
- 减少代码大小:禁用异常后,程序不需要生成额外的异常处理表。
- 提升性能:减少异常支持相关的运行时检查。
- 限制功能:所有
try-catch块、throw表达式以及捕获系统异常的能力都不可用。
风险
- 禁用异常后,程序中发生未捕获的错误将直接导致崩溃。
- 开发者需要依赖其他方式(如错误码)来处理异常情况。
使用场景
- 适合不依赖异常处理的小型项目或性能敏感的嵌入式系统。
- 项目中已经采用其他机制(如返回值或状态码)代替异常处理。
两者对比与结合
| 参数 | 功能 | 适用场景 | 限制 |
|---|---|---|---|
-GR- | 禁用 RTTI,减少运行时类型信息 | 性能优化或不使用动态类型查询时 | 禁用 typeid 和 dynamic_cast |
-EHa- | 禁用 C++ 异常和 SEH,减少异常支持开销 | 不依赖异常处理的性能优化场景 | 无法使用 try-catch 和异常机制 |
结合使用
-GR- -EHa-通常在对性能和代码大小极为敏感的项目中一起使用,例如嵌入式开发或实时系统。
示例
编译命令:
cl -GR- -EHa- source.cpp
代码限制示例:
以下代码在启用 -GR- 和 -EHa- 后会导致编译错误:
#include <iostream>
#include <typeinfo>class Base { virtual void func() {} };
class Derived : public Base {};int main() {try {throw std::runtime_error("Error"); // 会报错} catch (...) {std::cout << "Caught an exception\n";}Base* b = new Derived();if (typeid(*b) == typeid(Derived)) { // 会报错std::cout << "Derived type\n";}return 0;
}
编译器反馈:
- 禁用 RTTI 后,
typeid和dynamic_cast的使用会报错。 - 禁用异常后,
try-catch和throw的使用会报错。
总结
-GR-:禁用运行时类型信息,优化性能和减小代码体积。-EHa-:禁用异常处理支持,进一步减少代码开销,但需要谨慎处理错误情况。- 两者结合适用于不需要运行时类型检查和异常支持的高性能或嵌入式项目。

-EHa-:我貌似不会报错不知道为啥
# 为 MSVC 编译器添加参数
if(MSVC) # 如果编译器是 MSVC(Microsoft Visual C++)# 设置 C++ 编译选项set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -GR- -EHa- /WX /W4 /wd4819 /wd4201 /Z7 /FC")# /WX : 将所有警告视为错误(会让编译因警告失败)# /W4 : 设置警告级别为 4,显示大多数警告# /wd4819 : 屏蔽警告 C4819,避免文件编码问题导致的警告
endif()
在编译和链接程序时,/link 选项是用于 Microsoft Visual C++(MSVC)编译器 的一个特殊选项,主要用来向链接器传递参数。它通常用于在命令行中,将编译器选项与链接器选项分开。
使用场景
/link 后面跟随的是直接传递给链接器的参数,而不是编译器的选项。通过这种方式,可以在一次命令中同时指定编译器和链接器的设置。
示例
cl main.cpp /link /OUT:myprogram.exe
cl: MSVC 的编译器工具。main.cpp: 源代码文件。/link: 告诉编译器,接下来的参数是链接器选项。/OUT:myprogram.exe: 链接器选项,用于指定生成的可执行文件名称。
常见的链接器选项
以下是一些可以与 /link 一起使用的链接器选项:
/OUT:<filename>: 指定输出文件名称。/LIBPATH:<path>: 指定搜索库的路径。/SUBSYSTEM:<type>: 指定子系统类型(如CONSOLE或WINDOWS)。/DEBUG: 启用调试信息。/ENTRY:<function>: 指定程序入口点。
注意事项
- 顺序:
/link后的参数会直接传递给链接器,因此它必须位于链接器选项之前。 - 用途:
/link一般在需要自定义链接行为或指定复杂链接器选项时使用。
/MD 和 /MT 都是 Microsoft Visual C++ 编译器的选项,用于指定运行时库的链接方式。它们的区别在于使用的是 动态链接库(DLL)还是 静态链接库(.lib)。这两个选项控制如何处理 C++ 标准库和 C 运行时库的链接。
/MD - 动态链接运行时库(DLL)
- 解释:使用 动态链接库(DLL)来链接 C 运行时库和 C++ 标准库。
- 行为:编译器会链接到动态运行时库
msvcrt.dll(Microsoft C Runtime Library)。这意味着你的程序在运行时需要依赖于外部的动态链接库文件。 - 优点:生成的可执行文件较小,因为运行时库被共享,可以由多个程序同时使用。
- 缺点:在程序运行时需要确保目标系统上已经安装了相应版本的 DLL 文件,否则程序将无法启动。
/MT - 静态链接运行时库(静态库)
- 解释:使用 静态链接库 来链接 C 运行时库和 C++ 标准库。
- 行为:编译器会将运行时库和 C++ 标准库的代码直接嵌入到可执行文件中。这样,你的程序不需要依赖于任何外部动态链接库。
- 优点:生成的可执行文件完全独立,不依赖任何外部的 DLL 文件,这对于某些环境(如嵌入式系统)或需要部署独立程序的场景是有利的。
- 缺点:可执行文件会更大,因为运行时库的所有代码都被静态地链接进去了。此外,不同程序之间无法共享运行时库,因此如果多个程序使用相同的静态库,会导致冗余代码。
/MDd 和 /MTd(调试版本)
/MDd:动态链接调试版本的运行时库(DLL),通常用于调试模式。/MTd:静态链接调试版本的运行时库(静态库),通常用于调试模式。
总结
/MD:动态链接运行时库,程序运行时依赖msvcrt.dll,适用于需要共享运行时库的场景。/MT:静态链接运行时库,程序包含所有运行时库的代码,适用于希望程序完全独立的场景。
选择哪一个取决于项目的需求:
- 如果需要减小程序大小并共享库,使用
/MD。 - 如果需要完全独立的程序,且不依赖于外部 DLL 文件,使用
/MT。
-Gm- 是 Microsoft Visual C++ 编译器的一个选项,用于控制 生成程序的最小调试信息。
解释:
-Gm-禁用最小调试信息的生成。- 默认情况下,编译器会生成较为详细的调试信息,以便调试工具(如 Visual Studio 调试器)能够提供更全面的调试支持。
- 使用
-Gm-后,编译器会关闭这种调试信息生成,但它不会完全禁用所有调试信息,只是减少生成的调试信息量。
使用场景:
- 减少调试信息大小:在某些情况下,你可能希望生成较小的调试信息文件,以减小程序的尺寸,尤其是在发布版本中。
- 优化编译速度:禁用最小调试信息可能会略微提高编译速度,尤其是在大型项目中。
对比其他调试选项:
-Gm:启用最小调试信息,生成较少的调试信息,主要用于优化构建过程。-Zi:生成完整的调试信息,通常用于调试版本的构建。-Z7:生成最小调试信息,通常适用于调试工具,如在没有完整源代码时进行基本的调试。
总结
-Gm- 选项用于禁用最小调试信息的生成,在某些情况下可以减少调试信息的体积,但通常不推荐用于需要调试的场合。
在使用 -Fm 选项生成 .map 文件时,符号名称(如函数名、变量名)会被编译器名称修饰(名称改编或 mangling)。这是因为 C++ 编译器需要通过对符号名称进行修饰来支持函数重载、模板等特性。
1. C++ 符号名称修饰的原因:
C++ 是一种支持函数重载、模板和命名空间的语言。这意味着同一函数名可能会在不同的上下文中定义多个版本。例如:
- 同名函数的不同参数类型(函数重载)。
- 模板类或函数的实例化(泛型编程)。
为了区分这些函数或变量,编译器会对它们的名称进行修饰(mangling),即将函数名或变量名与其类型信息、模板参数等信息结合起来,形成唯一的标识符。
2. 符号名称修饰的表现:
在 .map 文件中,你会看到原本简单的函数名或变量名被改为一长串看似无意义的字符。这些字符是由编译器根据函数的参数类型、返回类型、命名空间等信息生成的,目的是避免不同的符号冲突。
例如:
- 一个简单的函数
int add(int, int)在.map文件中的名称可能会变成_Z3addiij,其中_Z3add表示函数名,ii表示两个int类型的参数,j表示返回值类型。
3. 修饰后的符号名称的含义:
_Z:标志名称是经过修饰的(由 C++ 编译器生成)。- 函数名和类型信息:修饰符中包含函数名以及参数的类型和顺序,甚至返回值的类型。
4. 如何避免名称修饰:
如果你不希望看到这些修饰的符号名称,可以考虑使用 extern "C" 来避免 C++ 的名称修饰。extern "C" 告诉编译器按 C 语言规则进行符号命名,不进行修饰。
例如:
extern "C" {int add(int a, int b) {return a + b;}
}
这种方式下,add 函数在 .map 文件中的名称将是 add,而不是修饰后的名称。
结论:
在使用 -Fm 选项生成 .map 文件时,符号名称被修饰是因为 C++ 编译器通过名称修饰来支持函数重载、模板、命名空间等特性。如果需要避免这种修饰,可以使用 extern "C" 来禁止名称修饰。
/Od 是 Microsoft Visual C++ 编译器的一个选项,用于禁用优化。
作用:
/Od表示 禁用所有优化(Disable optimization),即编译器不会进行任何优化操作,包括常见的如代码内联、循环展开、死代码删除等。- 这个选项通常用于调试时,以便让调试信息更准确,程序的行为更接近源代码的编写方式。禁用优化后,编译器不会对代码进行任何修改,便于开发者更容易地跟踪和调试程序的执行流程。
使用场景:
- 调试阶段:在调试程序时,禁用优化可以避免优化所带来的副作用(如变量的值被编译器优化掉、函数调用被内联等),使得调试时的行为和源代码一致。
- 排查问题:在排查程序错误时,禁用优化有助于确认程序的实际行为,因为优化可能会改变代码的执行顺序或变量的生命周期。
示例:
cl /Od myprogram.cpp
这个命令会编译 myprogram.cpp,并禁用所有优化。
总结:
/Od 选项用于禁用编译器的优化,通常在调试过程中使用,以确保调试信息与源代码更一致。
开始将键盘输入移到与平台无关的代码中





相关文章:
游戏引擎学习第16天
视频参考:https://www.bilibili.com/video/BV1mEUCY8EiC/ 这些字幕讨论了编译器警告的概念以及如何在编译过程中启用和处理警告。以下是字幕的内容摘要: 警告的定义:警告是编译器用来告诉你某些地方可能存在问题,尽管编译器不强制要求你修复…...
如何通过对敏捷实践的调整,帮助远程团队提升研发效能?
首先明确一点,最敏捷的做法就是不要远程团队或分布式团队,远程一定比不上面对面同一地点的模式,毕竟环境不同,就不要期望远程团队和本地团队具备相同的效能,甚至期望更高。 那么,无论何种原因,…...
Ubuntu Linux使用前准备动作 配置SSH
在 Ubuntu 系统中配置 SSH 服务可以通过以下步骤进行: 1、安装ssh服务 1)打开终端(可以使用快捷键 Ctrl Alt T)。 2)运行以下命令安装 OpenSSH 服务器: sudo apt-get update:这一步是更新…...
疫情下的图书馆管理系统:Spring Boot技术
摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了疫情下图书馆管理系统的开发全过程。通过分析疫情下图书馆管理系统管理的不足,创建了一个计算机管理疫情下图书馆管理系统的方案。文章介绍了疫情下图…...
vue3完整安装并创建项目
1、下载:https://npmmirror.com/mirrors/node/v18.19.0/node-v18.19.0-x64.msi 2、验证Nodejs是否安装成功(管理员身份运行cmd) node -v #查看nodejs的版本 v18.19.0npm -v #查看npm的版本 10.2.3 3、在D:\Program Files\nodejs路径下创建两…...
【Linux】Linux入门实操——进程管理(重点)
1. 概述 在 LINUX 中,每个执行的程序都称为一个进程。每一个进程都分配一个ID号(pid,进程号)。>windows > linux每个进程都可能以两种方式存在的。前台与后台,所谓前台进程就是用户目前的屏幕上可以进行操作的。后台进程则是实际在操作࿰…...
Linux-Apache
文章目录 Apache基础配置 🏡作者主页:点击! 🤖Linux专栏:点击! ⏰️创作时间:2024年11月19日12点20分 Apache Web服务器用来实现HTTP和相关TCP连接的处理,同时负责所提供资源的管理…...
高危,Laravel参数注入漏洞安全风险通告
今日,亚信安全CERT监控到安全社区研究人员发布安全通告,披露了Laravel 参数注入漏洞(CVE-2024-52301)。在受影响的版本中,Application.php 文件的 detectEnvironment 函数直接使用了 $_SERVER[argv],但没有检查运行环境是否为 CLI…...
【Qt】使用QString的toLocal8Bit()导致的问题
问题 使用Qt发送一个Http post请求的时候,服务一直返回错误和失败信息。同样的url以及post参数,复制黏贴到postman里就可以发送成功。就感觉很神奇。 原因 最后排查出原因是因为参数中含有汉字而导致的编码问题。 在拼接post参数时,使用了…...
Android上运行Opencv(TODO)
在高通安卓平台上,确实可以通过 NDK 使用 OpenCV 并访问摄像头。NDK 提供了更高性能的计算能力,特别是在图像处理和计算密集型任务中,与 OpenCV 结合可以充分利用高通平台的硬件资源(如 NEON SIMD 指令集和 GPU 加速)。…...
动态IP黑白名单过滤的设计与实现(上篇设计思想)
文章目录 需求分析方案设计1、设计过程2、最终方案3、扩展知识 - 布隆过滤器 需求分析 一些恶意用户(可能是黑客、爬虫、DDoS 攻击者)可能频繁请求服务器资源,导致资源占用过高。因此我们需要一定的手段实时阻止可疑或恶意的用户,…...
LeetCode 力扣 热题 100道(五)最长回文子串(C++)
最长回文子串 给你一个字符串 s,找到 s 中最长的 回文子串。 回文性 如果字符串向前和向后读都相同,则它满足 回文性 子字符串子字符串 是字符串中连续的 非空 字符序列。 动态规划法 class Solution { public:string longestPalindrome(string s) {i…...
Docker--Docker Registry(镜像仓库)
什么是Docker Registry? 镜像仓库(Docker Registry)是Docker生态系统中用于存储、管理和分发Docker镜像的关键组件。 镜像仓库主要负责存储Docker镜像,这些镜像包含了应用程序及其相关的依赖项和配置,是构建和运行Doc…...
maven手动上传jar到私服仓库:mvn deploy:deploy-file命令
一、场景 现需要将公司内部的jar包上传到私服仓库,供其他同事使用,此时就需要用到mvn deploy:deploy-file命令。 二、 mvn deploy:deploy-file命令 举个栗子: mvn deploy:deploy-file -DgroupIdorg.pttsql -DartifactIdpttsql -Dversi…...
【机器学习】机器学习中用到的高等数学知识-1.线性代数 (Linear Algebra)
向量(Vector)和矩阵(Matrix):用于表示数据集(Dataset)和特征(Feature)。矩阵运算:加法、乘法和逆矩阵(Inverse Matrix)等,用于计算模型参数。特征值(Eigenvalues)和特征向量(Eigenvectors)&…...
无插件H5播放器EasyPlayer.js网页web无插件播放器选择全屏时,视频区域并没有全屏问题的解决方案
EasyPlayer.js H5播放器,是一款能够同时支持HTTP、HTTP-FLV、HLS(m3u8)、WS、WEBRTC、FMP4视频直播与视频点播等多种协议,支持H.264、H.265、AAC、G711A、MP3等多种音视频编码格式,支持MSE、WASM、WebCodec等多种解码方…...
Idea中创建和联系MySQL等数据库
备注:电脑中要已下好自己需要的MySQL数据库软件 MySQL社区版下载链接: https://dev.mysql.com/downloads/installer/ 优点: 1.相比与在命令行中管理数据库,idea提供了图形化管理,简单明了; 2.便于与后端…...
【pytest】pytest注解使用指南
前言:在 pytest 测试框架中,注解(通常称为装饰器)用于为测试函数、类或方法提供额外的信息或元数据。这些装饰器可以影响测试的执行方式、报告方式以及测试的组织结构。pytest 提供了多种内置的装饰器,以及通过插件扩展…...
在Unity中使用Epplus写Excel
Overview 本文旨在帮助你快速入门,该库发展多年内容庞大(官方文档写的极好:https://github.com/EPPlusSoftware/EPPlus/wiki),有些功能在Unity环境可能你永远都不会使用. 官方的一个Demo: https://github.com/EPPlusSoftware/EPPlus.Samples.CSharp 如果你只有读的需求,可以…...
初识算法 · 模拟(2)
目录 前言: Z字形变换 题目解析 算法原理 算法编写 数青蛙 题目解析 算法原理 算法编写 前言: 本文的主题是模拟,通过两道题目讲解,一道是Z字形变化,一道是数青蛙。 链接分别为: 1419. 数青蛙…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
