游戏引擎学习第16天

视频参考:https://www.bilibili.com/video/BV1mEUCY8EiC/

这些字幕讨论了编译器警告的概念以及如何在编译过程中启用和处理警告。以下是字幕的内容摘要:

  1. 警告的定义:警告是编译器用来告诉你某些地方可能存在问题,尽管编译器不强制要求你修复它们。警告并不会阻止编译过程,但它们通常提示代码中可能存在潜在问题。

  2. 警告级别:警告通常分为不同的级别,例如1级、2级、3级、4级等。你可以选择启用一个警告级别,以便只关注特定类型的警告,而不是每一个单独的警告。

  3. 启用警告:作者提到通过启用较高级别的警告,可以让编译器显示更多的警告信息,帮助开发者发现潜在问题。

  4. 解决警告:一些警告是可以修复的,而其他警告则可能是由于外部库或系统文件(如 windows.h)引起的,在这些情况下可能无法轻易修复。

  5. 警告级别的调整:如果某些警告级别的警告过于繁琐,可以通过降低警告级别来减少警告的数量,从而使得编译过程更加顺利。

  6. 具体示例:提到了一些警告信息,例如编译器告诉开发者结构体中可能插入了填充,或者警告与 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 的一个警告,表明某个函数参数未被使用。在你的代码中,这些未使用的参数是 pStatepVibrationdwUserIndex,它们出现在以下打桩函数中:

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 警告:

  1. CMake 中添加编译选项:

    if(MSVC)
        add_compile_options(/wd4100) # 禁用 C4100 警告
    endif()
    
  2. 在代码中使用 #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:使用 __pragmaUNREFERENCED_PARAMETER 宏。
  • 若无需依赖编译器特性,可使用 (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 一起使用,尤其在调试场景中:

  1. 完整路径-FC)有助于清楚地定位源代码文件。
  2. 调试信息-Zi)提供详细的调试能力。
示例

如果你在 VS 中手动编译代码,可以添加如下命令:

cl -Zi -FC source.cpp

这将生成包含完整路径的调试信息文件(.pdb 文件)。

在 Visual Studio 的编译器中,-Oi 是一个优化选项,用于启用 内联函数调用,提高程序的性能。


作用

-Oi 参数启用编译器对特定内建函数(intrinsic functions)的内联替换。

  • 内建函数(Intrinsic Functions):是编译器提供的一些特殊函数,通常映射到特定的 CPU 指令。例如,memcpystrcmp 或数学函数如 sqrt
  • 启用 -Oi 后,这些函数在编译时被替换为等效的高效汇编指令,而不是通过函数调用完成任务。

优点

  1. 性能提升
    • 减少函数调用的开销(如栈操作和返回值处理)。
    • 提高执行效率,因为直接使用了 CPU 指令。
  2. 优化代码大小
    • 内联代码有时能减少代码膨胀,尤其是对于小函数。
  3. 使用高级指令
    • 编译器可能使用更高效的 CPU 指令来实现特定功能。

可能的缺点

  1. 代码大小增加
    • 如果过多的函数被内联,可能导致生成的代码体积增大。
  2. 兼容性问题
    • 某些内建函数可能与特定硬件架构相关,因此需要确保目标硬件支持。
  3. 调试复杂性
    • 内联后的代码可能难以逐步调试,因为它不再是显式的函数调用。

使用场景

  • 适用:高性能应用,尤其是对数学计算、字符串操作等性能敏感的场景。
  • 不适用:代码大小有限制或者需要清晰的调试信息时。

相关指令

-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++ 提供的一种运行时机制,允许程序在运行时动态查询对象的类型信息(例如使用 typeiddynamic_cast)。
  • 启用 RTTI(默认设置,-GR)会在程序中生成额外的类型信息。
禁用 RTTI 的效果
  • 减少代码大小:避免生成额外的类型信息,可以降低可执行文件的体积。
  • 提升性能:避免运行时类型检查带来的额外开销。
  • 限制功能:typeiddynamic_cast 无法使用,程序中尝试调用它们会导致编译错误。
使用场景
  • 适用于不依赖 RTTI 的代码,尤其是性能和代码大小敏感的项目(例如嵌入式开发)。
  • 代码完全避免了多态和运行时类型查询。

-EHa- 参数

作用:禁用对结构化异常处理(SEH)和 C++ 异常的支持。

SEH 和 C++ 异常简介
  • SEH:Windows 特有的异常机制,捕获系统级异常(如访问冲突)。
  • C++ 异常:C++ 标准支持的异常机制(try-catch 块)。
禁用异常支持的效果
  • 减少代码大小:禁用异常后,程序不需要生成额外的异常处理表。
  • 提升性能:减少异常支持相关的运行时检查。
  • 限制功能:所有 try-catch 块、throw 表达式以及捕获系统异常的能力都不可用。
风险
  • 禁用异常后,程序中发生未捕获的错误将直接导致崩溃。
  • 开发者需要依赖其他方式(如错误码)来处理异常情况。
使用场景
  • 适合不依赖异常处理的小型项目或性能敏感的嵌入式系统。
  • 项目中已经采用其他机制(如返回值或状态码)代替异常处理。

两者对比与结合

参数功能适用场景限制
-GR-禁用 RTTI,减少运行时类型信息性能优化或不使用动态类型查询时禁用 typeiddynamic_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 后,typeiddynamic_cast 的使用会报错。
  • 禁用异常后,try-catchthrow 的使用会报错。

总结

  • -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>: 指定子系统类型(如 CONSOLEWINDOWS)。
  • /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" 来禁止名称修饰。

/OdMicrosoft Visual C++ 编译器的一个选项,用于禁用优化。

作用:

  • /Od 表示 禁用所有优化(Disable optimization),即编译器不会进行任何优化操作,包括常见的如代码内联、循环展开、死代码删除等。
  • 这个选项通常用于调试时,以便让调试信息更准确,程序的行为更接近源代码的编写方式。禁用优化后,编译器不会对代码进行任何修改,便于开发者更容易地跟踪和调试程序的执行流程。

使用场景:

  • 调试阶段:在调试程序时,禁用优化可以避免优化所带来的副作用(如变量的值被编译器优化掉、函数调用被内联等),使得调试时的行为和源代码一致。
  • 排查问题:在排查程序错误时,禁用优化有助于确认程序的实际行为,因为优化可能会改变代码的执行顺序或变量的生命周期。

示例:

cl /Od myprogram.cpp

这个命令会编译 myprogram.cpp,并禁用所有优化。

总结:

/Od 选项用于禁用编译器的优化,通常在调试过程中使用,以确保调试信息与源代码更一致。

开始将键盘输入移到与平台无关的代码中

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/918079.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解析煤矿一张图

解析煤矿一张图 ​ 煤矿一张图是指通过数字化、智能化技术将煤矿的各项信息、数据和资源进行集中展示和管理&#xff0c;形成一个综合的可视化平台。这一平台将矿井的地理信息、设备状态、人员位置、安全生产、环境监测等信息整合成一个统一的“图形”&#xff0c;以便于管理者…

Python学习27天

字典 dict{one:1,two:2,three:3} # 遍历1&#xff1a; # 先取出Key for key in dict:# 取出Key对应的valueprint(f"key:{key}---value:{dict[key]}")#遍历2&#xff0c;依次取出value for value in dict.values():print(value)# 遍历3&#xff1a;依次取出key,value …

【伪造检测】Noise Based Deepfake Detection via Multi-Head Relative-Interaction

一、研究动机 [!note] 动机&#xff1a;目前基于噪声的检测是利用Photo Response Non-Uniformity (PRNU)实现的&#xff0c;这是一种由于相机感光传感器而造成的缺陷噪声&#xff0c;主要用图像的源识别&#xff0c;在伪造检测的任务中并没有很好的表现。因此在文中提出了一种基…

【eNSP】企业网络架构实验——vlan间的路由通信(三)

VLAN间的路由是指不同VLAN之间的通信&#xff0c;通常VLAN是用来分割网络流量和提高网络安全性的。 一、VLAN 1. 什么是VLAN&#xff1f; VLAN&#xff0c;全称是虚拟局域网&#xff08;Virtual Local Area Network&#xff09;&#xff0c;是一种将物理局域网&#xff08;LA…

github 模型下载方法

github 模型权重&#xff0c;如果是项目下载&#xff0c;pth文件有时下载后只有1kb 本人测试ok下载方法&#xff1a; 点击view raw&#xff0c;然后可以下载模型权重文件了。

【微软:多模态基础模型】(2)视觉理解

欢迎关注【youcans的AGI学习笔记】原创作品 【微软&#xff1a;多模态基础模型】&#xff08;1&#xff09;从专家到通用助手 【微软&#xff1a;多模态基础模型】&#xff08;2&#xff09;视觉理解 【微软&#xff1a;多模态基础模型】&#xff08;3&#xff09;视觉生成 【微…

Dolby TrueHD和Dolby Digital Plus (E-AC-3)编码介绍

文章目录 1. Dolby TrueHD特点总结 2. Dolby Digital Plus (E-AC-3)特点总结 Dolby TrueHD 与 Dolby Digital Plus (E-AC-3) 的对比 Dolby TrueHD和Dolby Digital Plus (E-AC-3) 是两种高级的杜比音频编码格式&#xff0c;常用于蓝光影碟、流媒体、影院等高品质音频传输场景。它…

基于SpringBoot的养老院管理系统+文档

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

Figma汉化:提升设计效率,降低沟通成本

在UI设计领域&#xff0c;Figma因其强大的功能而广受欢迎&#xff0c;但全英文界面对于国内设计师来说是一个不小的挑战。幸运的是&#xff0c;通过Figma汉化插件&#xff0c;我们可以克服语言障碍。以下是两种获取和安装Figma汉化插件的方法&#xff0c;旨在帮助国内的UI设计师…

JavaWeb之AJAX

前言 这一节讲JavaWeb之AJAX 1.概述 以前我们在servlet中得到数据&#xff0c;必须通过域给jsp&#xff0c;然后jsp在响应给浏览器 纯html不能获取servlet返回数据 所以我们用jsp 但是现在我们可以同AJAX给返回数据了 我们可以在sevlet中直接通过AJAX返回给浏览器 html中的J…

【Spring】Bean

Spring 将管理对象称为 Bean。 Spring 可以看作是一个大型工厂&#xff0c;用于生产和管理 Spring 容器中的 Bean。如果要使用 Spring 生产和管理 Bean&#xff0c;那么就需要将 Bean 配置在 Spring 的配置文件中。Spring 框架支持 XML 和 Properties 两种格式的配置文件&#…

[Python学习日记-68] 绑定方法与非绑定方法

[Python学习日记-68] 绑定方法与非绑定方法 简介 绑定方法 非绑定方法 绑定方法与非绑定方法的应用 简介 在之前我们学习类与对象的属性查找与绑定方法的时候就接触过绑定方法了&#xff0c;不过当时是简单的介绍了针对于对象的绑定方法&#xff0c;其实在类内部定义的函数…

逆向攻防世界CTF系列39-debug

逆向攻防世界CTF系列39-debug 查了资料说.NET要用其它调试器&#xff0c;下载了ILSPY和dnSPY ILSPY比较适合静态分析代码最好了&#xff0c;函数名虽然可能乱码不显示&#xff0c;但是单击函数名还是能跟踪的&#xff0c;而dnSPY在动态调试上效果好&#xff0c;它的函数名不仅…

Spring-事务学习

spring事务 1. 什么是事务? 事务其实是一个并发控制单位&#xff0c;是用户定义的一个操作序列&#xff0c;这些操作要么全部完成&#xff0c;要不全部不完成&#xff0c;是一个不可分割的工作单位。事务有 ACID 四个特性&#xff0c;即&#xff1a; 原子性&#xff08;Atom…

RHCE的学习(21)

第三章 Shell条件测试 用途 为了能够正确处理Shell程序运行过程中遇到的各种情况&#xff0c;Linux Shell提供了一组测试运算符。 通过这些运算符&#xff0c;Shell程序能够判断某种或者几个条件是否成立。 条件测试在各种流程控制语句&#xff0c;例如判断语句和循环语句中…

用pyspark把kafka主题数据经过etl导入另一个主题中的有关报错

首先看一下我们的示例代码 import os from pyspark.sql import SparkSession import pyspark.sql.functions as F """ ------------------------------------------Description : TODO&#xff1a;SourceFile : etl_stream_kafkaAuthor : zxxDate : 2024/11/…

单片机_day3_GPIO

目录 1. 灯如何才能亮 1.1原理图 1.2 二极管 1.3 换了一个灯和原理图 ​编辑 1.4 三极管 1.4.1 NPN型三极管 1.4.2 PNP型三极管 2. 基本概念 3. 输入 3.1 浮空输入 3.2 上拉输入 3.3 下拉输入 3.4 模拟输入 4. 输出 4.1 推挽输出 4.2 开漏输出 如何让开漏输出…

基于视觉智能的时间序列基础模型

GitHub链接&#xff1a;ViTime: A Visual Intelligence-Based Foundation Model for Time Series Forecasting 论文链接&#xff1a;https://github.com/IkeYang/ViTime 前言 作者是来自西安理工大学&#xff0c;西北工业大学&#xff0c;以色列理工大学以及香港城市大学的研…

java项目-jenkins任务的创建和执行

参考内容: jenkins的安装部署以及全局配置 1.编译任务的general 2.源码管理 3.构建里编译打包然后copy复制jar包到运行服务器的路径 clean install -DskipTests -Pdev 中的-Pdev这个参数用于激活 Maven 项目中的特定构建配置&#xff08;Profile&#xff09; 在 pom.xml 文件…

Qt按钮类-->day09

按钮基类 QAbstractButton 标题与图标 // 参数text的内容显示到按钮上 void QAbstractButton::setText(const QString &text); // 得到按钮上显示的文本内容, 函数的返回就是 QString QAbstractButton::text() const;// 得到按钮设置的图标 QIcon icon() const; // 给按钮…