2 Choosing the optimal platform

2.1 Choice of hardware platform
与过去相比,硬件平台的选择变得不那么重要了。在RISC和CISC处理器、个人计算机和大型机之间,以及简单处理器和向量处理器之间的区别越来越模糊,因为标准的个人计算机处理器具有CISC指令集、RISC核心、向量处理指令、多个核心,并且处理速度超过昨天的大型主机计算机。
如今,选择特定任务的硬件平台往往是基于价格、兼容性、备份供应商以及可用的良好开发工具等考虑因素,而不是基于处理能力。将几台标准个人计算机连接在网络中可能既更便宜又更高效,而不是投资一台大型主机计算机。具有大规模并行向量处理能力的大型超级计算机在科学计算领域仍然有一席之地,但对于大多数用途来说,由于性能/价格比较优越,标准个人计算机处理器更受青睐。
从技术角度来看,标准个人计算机处理器的CISC指令集(x86)不是最优的。为了向后兼容于追溯到1980年左右的软件系列,保留了这个指令集。然而,CISC指令集比其声誉更好。代码的紧凑性使得缓存更加高效,而缓存大小是有限资源。在代码缓存至关重要的情况下,CISC指令集实际上可能比RISC更好。CISC指令集的另一个优势是执行相同任务所需的指令较少。原始x86指令集的一个严重问题是寄存器的稀缺性。这个问题在x86指令集的64位扩展中得到了缓解,通用寄存器数量翻倍,矢量寄存器数量随AVX512指令集扩展增加了四倍。
依赖网络资源的瘦客户机不适合用于关键应用程序,因为无法控制网络资源的响应时间。
小型手持设备变得越来越受欢迎,并且用于日益增多的任务,例如电子邮件和网页浏览,在以前需要个人计算机才能完成的任务。同样,我们也看到越来越多的嵌入式微控制器设备和机器的出现。我不会具体推荐哪种平台和操作系统对这类应用程序最高效,但重要的是要意识到这些设备通常比个人计算机具有更少的内存和计算能力。因此,在这些系统上节约资源的使用比在个人计算机上更加重要。然而,通过良好优化的软件设计,即使在这些小型设备上,许多应用程序也可以实现良好的性能,正如第173页所讨论的那样。
本手册基于标准的个人计算机平台,使用英特尔、AMD或VIA处理器和运行在32位或64位模式下的Windows、Linux、BSD或Mac操作系统。这里给出的许多建议可能也适用于其他平台,但示例仅在个人计算机平台上进行了测试。
图形加速器
选择平台显然受到任务需求的影响。例如,一个需要大量图形处理的应用程序最好在配有图形协处理器或图形加速器卡的平台上实现。有些系统还配备了专用的物理处理器,用于计算计算机游戏或动画中物体的物理运动。
在某些情况下,可以利用图形加速器卡上处理器的高处理能力来执行除了在屏幕上渲染图形之外的其他任务。然而,这类应用程序高度依赖于系统,因此如果可移植性很重要的话不建议使用。本手册不涵盖图形处理器的内容。
可编程逻辑设备
可编程逻辑设备是一种可以用硬件定义语言(如Verilog或VHDL)进行编程的芯片。常见的设备有CPLD和FPGA。软件编程语言(如C++)与硬件定义语言的区别在于,软件编程语言定义了顺序指令的算法,而硬件定义语言定义了由数字构建模块(如门、触发器、多路复用器、算术单元等)和连接它们的电线组成的硬件电路。硬件定义语言固有地是并行的,因为它定义的是电气连接而不是操作序列。
与在微处理器中执行复杂数字操作相比,可编程逻辑设备通常能更快地执行这些操作,因为硬件可以针对特定目的进行连线。在某些情况下,可以将微处理器实现在FPGA中,作为所谓的软处理器。这样的软处理器比专用微处理器慢得多,因此本身并不具有优势。但是,当软处理器激活以硬件定义语言编写的关键应用特定指令时,可以在同一芯片中实现非常高效的解决方案。更强大的解决方案是将专用微处理器核心和FPGA组合在同一芯片中。这种混合解决方案现在在一些嵌入式系统中使用。
通过我的水晶球,我预见到类似的解决方案可能有一天会在个人计算机处理器中实现。应用程序将能够定义可以在硬件定义语言中编码的应用特定指令。这样的处理器将在代码缓存和数据缓存之外还具有硬件定义代码的额外缓存。
2.2 Choice of microprocessor
由于激烈的竞争,竞争品牌的微处理器在基准性能上非常相似。具备多个核心的处理器对于可以被划分为多个并行线程的应用程序来说具有优势。小型轻量级功耗低的处理器实际上有很大的计算能力,并且可能足够满足较低强度的应用需求。
2.3 Choice of operating system
所有新的x86系列微处理器都可以在16位、32位和64位模式下运行。16位模式曾在旧操作系统DOS和Windows 3.x中使用。这些系统在程序或数据大小超过64KB时使用内存分段,对于较大的程序来说效率非常低下。现代微处理器并未针对16位模式进行优化,而且一些操作系统对16位程序不向后兼容。除了用于小型嵌入式系统外,不建议开发16位程序。
64位操作系统如今很常见。这些系统能够运行32位和64位程序。对于某些调用函数频繁、使用大量RAM内存的CPU密集型应用程序来说,64位系统可以提高性能5-10%。如果性能瓶颈在其他地方,那么32位和64位系统之间的性能没有区别。使用大量内存的应用程序将从64位系统更大的地址空间中受益。
对于32位软件,Windows和Linux操作系统的性能几乎相同,因为这两个操作系统使用相同的函数调用约定。这里提到的有关Linux的内容也适用于BSD系统。
基于英特尔的Mac OS X操作系统基于BSD,但编译器默认使用位置无关代码和延迟绑定,这使其效率较低。通过使用静态链接和不使用位置无关代码(选项-fno-pic),可以提高性能。
64位系统相比32位系统具有几个优点:
• 寄存器数量加倍。这样就可以将中间数据和局部变量存储在寄存器中,而不是内存中。如果启用了AVX512指令集,64位模式下的矢量寄存器数量甚至更多。
• 函数参数通过寄存器传递,而不是通过堆栈。这使得函数调用更高效。
• 整数寄存器的大小扩展到64位。这在使用64位整数的应用程序中是一个优势。
• 大内存块的分配和释放更高效。
• 所有64位CPU和操作系统都支持SSE2指令集。
• 64位指令集支持数据的相对地址寻址。这使得位置无关代码更高效。
与32位系统相比,64位系统有以下缺点:
• 指针、引用和堆栈条目使用64位而不是32位。这使得数据缓存效率降低。
• 如果图像基址不能保证小于231,则在64位模式下访问静态或全局数组需要进行一些额外的地址计算指令。
• 在大内存模型中,代码和数据的总大小可以超过2GB,地址计算更为复杂。然而,很少使用大内存模型。
• 一些指令在64位模式下比32位模式多一个字节的长度。
通常情况下,如果有许多函数调用、大量大内存块分配或者程序可以利用64位整数计算,你可以期望64位程序比32位程序运行更快。如果程序使用超过2GB的数据,则必须使用64位系统。
在64位模式下,操作系统之间的相似性消失,因为函数调用约定不同。64位Windows只允许将四个函数参数传递给寄存器,而64位Linux、BSD和MacOS允许最多将14个参数传递给寄存器(6个整数和8个浮点数)。在64位Linux中,函数调用更高效的细节还有其他一些(请参见第49页和手册5:“不同C ++编译器和操作系统的调用约定”)。在许多函数调用的应用程序中,64位Linux可能比64位Windows运行略快。可以通过将关键函数定义为内联或静态,或者使用能够进行整体程序优化的编译器来减轻64位Windows的劣势。
2.4 Choice of programming language
在开始一个新的软件项目之前,选择最适合该项目的编程语言非常重要。低级语言适用于优化执行速度或程序大小,而高级语言适用于编写清晰和结构良好的代码,并且便于快速、简便地开发用户界面以及与网络资源、数据库等进行接口交互。
最终应用程序的效率取决于编程语言的实现方式。当代码被编译并分发为二进制可执行代码时,可以获得最高的效率。大多数C++、Pascal和Fortran的实现都基于编译器。
还有一些编程语言使用解释形式进行实现。程序代码按原样分发,并在运行时逐行进行解释。例如JavaScript、PHP、ASP和UNIX shell脚本。解释的代码非常低效,因为循环体在每次迭代中都需要重新解释一次。
一些实现使用即时编译(just-in-time compilation)。程序代码按原样分发和存储,并在执行时进行编译。Perl就是一个例子。
几种现代编程语言使用了中间代码(字节码)。源代码被编译为中间代码,并将其作为分发的代码。中间代码不能直接执行,而是必须经过第二步的解释或编译才能运行。Java的某些实现基于解释器,通过模拟所谓的Java虚拟机来解释中间代码。最好的Java虚拟机会对代码中最常用的部分进行即时编译。C#、托管的C++和其他Microsoft .NET框架中的语言基于中间代码的即时编译。
使用中间代码的原因是它旨在具备平台无关性和紧凑性。使用中间代码的最大缺点是用户必须安装一个大型的运行时框架来解释或编译中间代码。这个框架通常比代码本身使用更多资源。
中间代码的另一个缺点是它增加了额外的抽象层,使得详细的优化变得更加困难。另一方面,即时编译器可以针对它正在运行的CPU进行优化,而在预编译代码中实现特定于CPU的优化则更加复杂。
编程语言的历史和实现展示了一个起伏不定的过程,反映了效率、平台独立性和开发的冲突考虑。例如,最早的个人电脑上有一个Basic解释器。不久之后,Basic的编译器就出现了,因为解释版本的Basic太慢了。如今,最流行的Basic版本是Visual Basic .NET,它使用中间代码和即时编译进行实现。一些早期的Pascal实现使用了像今天Java所使用的中间代码。但是,当一款真正的编译器问世时,这种语言的流行度显著提高。
从这段讨论可以明确,选择编程语言是效率、可移植性和开发时间之间的妥协。当效率至关重要时,解释型语言是不可行的。基于中间代码和即时编译的语言可能是一种可行的折衷方案,特别是在移植性和开发易用性比速度更重要的情况下。这包括诸如C#、Visual Basic .NET和最好的Java实现等语言。然而,这些语言的缺点是需要加载非常庞大的运行时框架,每次运行程序时都必须加载。加载框架和编译程序所需的时间通常比执行程序所需的时间长得多。在运行时,运行时框架可能比程序本身使用更多的资源。使用这样的框架的程序有时在简单任务(如按下按钮或移动鼠标)的响应时间上会非常长。当速度至关重要时,应该明确避免使用.NET框架。
最快的执行无疑是通过完全编译的代码实现的。编译语言包括C、C++、D、Pascal、Fortran和其他一些较不知名的语言。我个人偏好C++,原因有几个。C++受到一些非常好的编译器和优化函数库的支持。C++是一种高级语言,具有其他语言很少见的许多高级特性。但是,C++语言也包含了低级别的C语言作为子集,从而可以访问底层优化。大多数C++编译器都能生成汇编语言输出,这对于检查编译器对代码进行了多好的优化很有用。此外,大多数C++编译器在需要最高级别优化时允许类似汇编的内置函数,内联汇编或轻松链接到汇编语言模块。从可移植性的角度来看,C++语言在所有主要平台上都有编译器。Pascal具有与C++类似的许多优点,但不太灵活。Fortran也相当高效,但语法非常老旧。
由于强大的开发工具的可用性,使用C++进行开发非常高效。一种流行的开发工具是Microsoft Visual Studio。该工具可以生成两种不同的C++实现,直接编译代码和中间代码,用于.NET框架的公共语言运行时。显然,当速度很重要时,直接编译版本是首选项。
C++的一个重要缺点与安全性有关。它没有对数组越界、整数溢出和无效指针进行检查。这些检查的缺失使得代码执行速度比其他有这些检查的语言更快。但程序员有责任在无法通过程序逻辑排除这些错误的情况下对其进行显式检查。下面是一些建议,第14页提供了更多信息。
当性能优化具有较高优先级时,C++绝对是首选的编程语言。与其他编程语言相比,性能的提升可以是相当大的。当性能对最终用户很重要时,性能的提升可以很容易地证明可能产生的开发时间略微增加是合理的。
可能会有一些情况需要基于中间代码的高级框架,但其中一部分代码仍然需要仔细优化。在这种情况下,混合实现可以是可行的解决方案。代码中最关键的部分可以使用编译的C++实现,而其余部分(包括用户界面等)可以在高级框架中实现。优化的代码部分可以编译为动态链接库(DLL)或共享对象,由其余的代码进行调用。这不是一个最优的解决方案,因为高级框架仍然需要大量资源,并且两种代码之间的转换会带来额外的开销。但是,如果时间关键的代码可以完全包含在已编译的DLL或共享对象中,这种解决方案仍然可以显著提高性能。
2.5 Choice of compiler
C++编译器在最近几十年变得越来越复杂。随着AVX512指令集扩展用于高级向量操作,我们现在有数千个不同的机器指令。C++语言标准中的许多新高级特性也增加了复杂性。与此同时,市场上的C++编译器数量减少了,一些过去流行的旧编译器现在要么过时了,要么已经停产。我怀疑未来市场上的编译器数量可能会进一步减少。
下面提到了一些最新的用于x86和x86-64平台的编译器。这些编译器都支持32位和64位代码以及自动向量化。
Microsoft Visual Studio
这是一个非常用户友好的编译器,具有许多功能和良好的调试器。完整版非常昂贵,但有一个免费的限制非商业版本可供使用。Visual Studio可以为各种平台构建使用各种编程语言(C++、C#、Visual Basic)的代码。在代码优化方面,Visual Studio并不是最好的编译器,但在项目开发阶段非常有用。如果性能很重要,您可以在发布版本的代码中使用其他编译器。可以使用Visual Studio集成开发环境(IDE)与其他编译器配合使用。
建议使用Clang编译器的插件。
Gnu
gcc或Gnu编译器是最好的优化编译器之一。它比Visual Studio不太友好,有时您必须是一个真正的极客才能理解众多的命令行选项。这个编译器是免费和开源的,几乎适用于任何平台。独立编译器从命令行运行,但也有多个集成开发环境(IDE)可用,包括Eclipse、NetBeans、CodeBlocks等等。
Clang
Clang编译器是一个非常好的优化编译器,在许多情况下,它实际上是最好的。Clang与Gnu编译器非常相似,拥有相同的功能和选项。Clang是基于x86的Mac平台上最常见的编译器,但也支持Linux和Windows平台。
Windows的Clang编译器有不同的版本,目前最好的版本是作为Microsoft Visual Studio 17或更高版本的插件提供的版本。
Cygwin64版本的Clang默认使用中等内存模型。这是相当浪费的,因为它会使用64位绝对地址而不是32位相对地址来处理静态变量和常量。您可以通过指定-mcmodel=small来改进性能。只有在正在进行对外部DLL内的变量的直接链接时(这是不好的编程实践),才需要中等内存模型。Cygwin版本的另一个缺点是,在分发可执行文件时,您必须包含Cygwin DLL。
Intel C++编译器
这个编译器没有自己的IDE。它是作为Microsoft Visual Studio在Windows上编译或在Eclipse在Linux上编译时的插件而设计的。也可以作为独立编译器从命令行或make工具中调用。它支持Windows和Linux以及基于Intel的Mac OS。
Intel编译器有两个版本:一个名为“Classic”的传统版本,以及一个名为Intel oneAPI LLVM-based compiler的新版本。后者是Clang编译器的衍生版本,并且行为非常类似于Clang。它比Classic版本能更好地进行优化。
“Classic” Intel编译器支持自动CPU调度,为不同的Intel CPU生成多个代码版本。Classic Intel编译器最重要的缺点是代码针对特定的Intel CPU进行优化。编译的代码会检查它是否在Intel CPU上运行。如果检测到其他品牌的CPU(如AMD或VIA),则会运行一个不同的代码分支,速度会降低,即使最佳的代码分支与处理器兼容。
评论
根据我的测试,开源编译器Clang和Gnu在代码优化方面是最好的。有关上述编译器详细测试,请参见第76页。
所有这些编译器都可以作为命令行版本使用,而无需使用IDE。商业编译器提供免费试用版本或简化版本。
如果使用相同平台和类似选项对它们进行编译,通常可以混合使用来自不同编译器的目标文件。Intel编译器生成与Microsoft Visual Studio或Gnu编译器兼容的目标文件,具体取决于平台。Gnu和Clang编译器的目标文件也是相互兼容的。其他组合需要声明extern "C"以确保链接函数的兼容性。
一些旧的编译器使用不同的目标文件格式,与上述编译器不兼容。
选择编译器在某些情况下可能取决于与遗留代码的兼容性要求、对IDE的特定偏好、调试工具、易于GUI开发、数据库集成、Web应用程序集成、混合语言编程等。如果所选编译器不能提供最佳优化,则可能需要使用不同的编译器来创建最关键的模块。如果主项目是用不同的编程语言编写的,那么可能需要将优化的C++代码放入动态链接库(.DLL或.so)中。否则,静态链接是首选的。
2.6 Choice of function libraries
在许多应用程序中,执行库函数占据了大部分的执行时间。耗时的库函数通常属于以下几个类别:
• 文件输入/输出
• 图形和声音处理
• 内存和字符串操作
• 数学函数
• 加密、解密、数据压缩
大多数编译器都包含针对这些目的的标准库。然而,标准库并不总是完全优化的。
库函数通常是被许多用户在许多不同应用程序中使用的小型代码片段。因此,将更多的精力投入到优化库函数中,而不是优化特定应用程序的代码,是值得的。最好的函数库是经过高度优化的,使用自动CPU调度(参见第135页)来支持最新的指令集扩展。
如果分析(参见第16页)显示某个应用程序在库函数中使用了大量的CPU时间,或者这一点是显而易见的,那么通过使用不同的函数库可能会显著提高性能。如果应用程序使用大部分时间在库函数中,那么除了找到最高效的库并节约库函数调用之外,可能不需要再进行其他优化。建议尝试不同的库,看哪个效果最好。
下面讨论了一些常见的函数库,还有许多用于特定目的的库可供使用。
Microsoft
随Microsoft编译器提供。某些函数已经优化得很好,而其他函数则没有。支持32位和64位Windows。
Gnu
随Gnu编译器提供。64位版本比32位版本更好。Gnu编译器经常插入内置代码,而不是最常用的内存和字符串指令。内置代码并不总是最优的。使用选项-fno-builtin可以获取库版本。
Mac
Mac OS X(Darwin)附带的Gnu编译器中的库是Xnu项目的一部分。一些最重要的函数包含在操作系统内核的所谓"commpage"中。这些函数针对Intel Core和以后的Intel处理器进行了高度优化。不支持AMD处理器和早期的Intel处理器。只能在Mac平台上运行。
Intel
Intel编译器包含标准函数库。还提供了几个专用的函数库,如"Intel Math Kernel Library"和"Integrated Performance Primitives"。这些函数库针对大型数据集进行了高度优化。Intel库针对Intel处理器的性能进行了最佳优化,但在AMD处理器上通常也可以提供满意的性能,除非与Intel编译器的"Classic"版本一起使用。有关在非Intel处理器上使用Intel编译器和库的讨论,请参见第143页。
AMD
AMD核心数学库包含了经过优化的数学函数。它也适用于Intel处理器。性能不如Intel库。
Asmlib
我自己的函数库,用于演示目的。可以从www.agner.org/optimize/asmlib.zip获取。目前包括经过优化的内存和字符串函数以及其他一些难以在其他地方找到的函数。在最新的处理器上运行时比许多其他库更快。支持所有x86和x86-64平台。

附件:asmlib.zip


2.7 Choice of user interface framework
在典型软件项目中,大部分代码用于用户界面。对于不需要大量计算的应用程序来说,CPU时间可能更多地用于用户界面而不是程序的核心任务。
应用程序员很少从头开始编写自己的图形用户界面。这不仅会浪费程序员的时间,也对最终用户不方便。为了提高可用性,菜单、按钮、对话框等应尽量标准化。程序员可以使用操作系统提供的标准用户界面元素,或者使用编译器和开发工具提供的库。
对于Windows和C++,一个流行的用户界面库是Microsoft Foundation Classes(MFC)。Linux系统有几个图形界面框架可供选择。用户界面库可以链接为运行时动态链接库(DLL)或静态库。与多个应用程序同时使用同一个DLL相比,运行时DLL占用更多内存资源。
用户界面库的大小可能超过应用程序本身,并且加载所需的时间更长。一种轻量级的替代方案是Windows Template Library(WTL)。与MFC应用程序相比,WTL应用程序通常更快、更紧凑。但由于文档质量不佳和缺乏高级开发工具,开发WTL应用程序可能需要更多时间。
还有几个跨平台的用户界面库,例如Qt和wxWidgets。对于可能在多个平台和操作系统上运行的应用程序,这些库非常有用。
最简单的用户界面是通过去掉图形用户界面并创建控制台模式程序来实现的。控制台模式程序的输入通常在命令行或输入文件中指定,输出则显示在控制台或输出到文件中。控制台模式程序开发简单、快速、紧凑。由于不依赖于特定于系统的图形界面调用,因此很容易在不同平台上移植。然而,由于缺乏自解释的菜单,可用性可能较差。控制台模式程序适用于从其他应用程序(如make工具)调用。
总结起来,用户界面框架的选择必须在开发时间、可用性、程序紧凑性和执行时间之间取得折中。没有一种通用的解决方案适用于所有应用程序。
2.8 Overcoming the drawbacks of the C++ language
虽然C++在优化方面有很多优点,但也存在一些缺点,导致开发者选择其他编程语言。这部分讨论了在选择C++进行优化时如何克服这些缺点。
可移植性:
就语法而言,C++是完全可移植的,并且在所有主要平台上都得到了标准化和支持。然而,C++也是一种允许直接访问硬件接口和系统调用的语言,这些当然是与系统相关的。为了便于在不同平台之间进行移植,建议将用户界面和其他系统特定部分的代码放在一个单独的模块中,并将任务特定的、假设是与系统无关的代码放在另一个模块中。
整数的大小和其他与硬件相关的细节取决于硬件平台和操作系统。详细内容请参阅第29页。
开发时间:
一些开发者认为某种特定的编程语言和开发工具比其他工具更快速。尽管其中的一部分差异只是习惯问题,但确实有一些开发工具具备强大的功能,可以自动完成大量琐碎的编程工作。通过采用一致的模块化和可重用的类,可以提高C++项目的开发时间和可维护性。
安全性:
C++语言最严重的问题与安全有关。标准C++实现没有对数组边界违规和无效指针进行检查。这是C++程序中频繁出现的错误源,也是黑客攻击的可能入口。为了防止在安全性要求高的程序中出现此类错误,有必要遵循一定的编程原则。
通过使用引用而不是指针、将指针初始化为零、在指向的对象变为无效时将指针设置为零,并避免指针算术和指针类型转换,可以避免与无效指针相关的问题。另外,可以用更高效的容器类模板替代通常使用指针的链表和其他数据结构,详细说明请参见第97页。避免使用scanf函数。
通过解决这些挑战,C++仍然可以成为优化的选择,提供高性能和效率,同时减轻潜在的缺点。
在C++程序中,数组越界违规可能是最常见的错误之一。超出数组末尾的写入操作可能会覆盖其他变量,甚至更糟糕的是,可能会覆盖定义数组的函数的返回地址。这可能导致各种奇怪和意外的行为。数组通常用作存储文本或输入数据的缓冲区。忽略对输入数据的缓冲区溢出检查是一种常见的错误,黑客经常利用此类漏洞进行攻击。
预防此类错误的好方法是用容器类替代数组。第97页讨论了不同容器类的效率问题。
文本字符串特别有问题,因为字符串长度可能没有确定的限制。在将每个字符串存储之前,使用字符数组存储字符串的旧式C风格方法虽然快速高效,但如果不检查字符串的长度,就不安全。解决此问题的标准方法是使用字符串类,如string或CString。这种方法安全且灵活,但在大型应用程序中效率较低。字符串类在创建或修改字符串时每次都会分配一个新的内存块。这可能导致内存碎片化,并涉及高额的堆管理和垃圾回收开销。一种更高效且不妥协安全性的解决方案是将所有字符串存储在一个内存池中。在附录的示例中,可以了解如何在内存池中存储字符串。

附件:cppexamples.zip

整数溢出是另一个安全问题。官方C标准中说,对于有符号整数,溢出时的行为是“未定义”的。这使得编译器可以忽略溢出或假设溢出不会发生。在Gnu编译器的情况下,假设有符号整数溢出不会发生的假设会不幸地导致编译器优化溢出检查。针对此问题有几种可能的解决方法:(1) 在溢出发生之前检查溢出,(2) 使用无符号整数-它们保证会环绕回来,(3) 使用选项-ftrapv捕获整数溢出,但这样效率极低,(4) 使用选项-Wstrict-overflow=2获得此类优化的编译器警告,或者(5) 使用选项-fwrapv或-fno-strict-overflow使溢出行为具有明确定义。
在代码的关键部分,速度很重要时,您可以偏离上述安全建议。如果不安全的代码仅限于经过充分测试、具有与程序的其他部分定义良好接口的函数、类、模板或模块,则可以容忍这种情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值