Understand Debug Version and Release Version

本文详细探讨了Debug和Release版本的本质区别,分析了Release版本出错的常见原因,并提供了调试Release版程序的有效策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

经常在 优快云 上看见有人问 Debug 运行正常但 Release 失败的问题。以往的讨论往往是经验性的,并没有指出会这样的真正原因是什么,要想找出真正的原因通常要凭运气。最近我看了一些这方面的书,又参考了 优快云 上的一些帖子,然后深入研究了一下关于二者的不同。以下是我的一些体会,拿来与大家共享。 -------------------------------------- 本文主要包含如下内容:

 

1. Debug Release 编译方式的本质区别

2. 哪些情况下 Release 版会出错

3. 怎样"调试" Release 版的程序

 

 

一、关于DebugRelease之本质区别的讨论

Debug Release 编译方式的本质区别 Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。 Debug Release 的真正秘密,在于一组编译选项。

 

下面列出了分别针对二者的选项(当然除此之外还有其他一些,如/Fd /Fo,但区别并不重要,通常他们也不会引起 Release 版错误,在此不讨论)

Debug 版本: /MDd /MLd /MTd 使用 Debug runtime library(调试版本的运行时刻函数库) /Od 关闭优化开关 /D "_DEBUG" 相当于 #define _DEBUG,打开编译调试代码开关(主要针对 assert函数) /ZI 创建 Edit and continue(编辑继续)数据库,这样在调试过程中如果修改了源代码不需重新编译 /GZ 可以帮助捕获内存错误 /Gm 打开最小化重链接开关,减少链接时间.

Release 版本: /MD /ML /MT 使用发布版本的运行时刻函数库 /O1 /O2 优化开关,使程序最小或最快 /D "NDEBUG" 关闭条件编译调试代码开关(即不编译assert函数) /GF 合并重复的字符串,并将字符串常量放到只读内存,防止被修改实际上,Debug Release 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。

 

 

 

  二、哪些情况下 Release 版会出错有了上面的介绍,我们再来逐个对照这些选项看看 Release 版错误是怎样产生的

1. Runtime Library

链接哪种运行时刻函数库通常只对程序的性能产生影响。调试版本的 Runtime Library 包含了调试信息,并采用了一些保护机制以帮助发现错误,因此性能不如发布版本。编译器提供的 Runtime Library 通常很稳定,不会造成 Release 版错误;倒是由于 Debug Runtime Library 加强了对错误的检测,如堆内存分配,有时会出现 Debug 有错但 Release 正常的现象。应当指出的是,如果 Debug 有错,即使 Release 正常,程序肯定是有 Bug 的,只不过可能是 Release 版的某次运行没有表现出来而已。

2. 优化:这是造成错误的主要原因,因为关闭优化时源程序基本上是直接翻译的,而打开优化后编译器会作出一系列假设。这类错误主要有以下几种:

(1) 帧指针(Frame Pointer)省略(简称 FPO ):在函数调用过程中,所有调用信息(返回地址、参数)以及自动变量都是放在栈中的。若函数的声明与实现不同(参数、返回值、调用方式),就会产生错误————但 Debug 方式下,栈的访问通过 EBP 寄存器保存的地址实现,如果没有发生数组越界之类的错误(或是越界"不多"),函数通常能正常执行;Release 方式下,优化会省略 EBP 栈基址指针,这样通过一个全局指针访问栈就会造成返回地址错误是程序崩溃。C++ 的强类型特性能检查出大多数这样的错误,但如果用了强制类型转换,就不行了。你可以在 Release 版本中强制加入 /Oy- 编译选项来关掉帧指针省略,以确定是否此类错误。此类错误通常有:

 

  MFC 消息响应函数书写错误。

正确的应为 afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam); ON_MESSAGE 宏包含强制类型转换。防止这种错误的方法之一是重定义 ON_MESSAGE 宏,把下列代码加到 stdafx.h 中(在#include "afxwin.h"之后),函数原形错误时编译会报错

#undef ON_MESSAGE

#define ON_MESSAGE(message, memberFxn) / { message, 0, 0, 0, AfxSig_lwl, / (AFX_PMSG)(AFX_PMSGW)(static_cast< LRESULT (AFX_MSG_CALL / CWnd::*)(WPARAM, LPARAM) > (&memberFxn) },

 

(2) volatile 型变量:volatile 告诉编译器该变量可能被程序之外的未知方式修改(如系统、其他进程和线程)。优化程序为了使程序性能提高,常把一些变量放在寄存器中(类似于 register 关键字),而其他进程只能对该变量所在的内存进行修改,而寄存器中的值没变。如果你的程序是多线程的,或者你发现某个变量的值与预期的不符而你确信已正确的设置了,则很可能遇到这样的问题。这种错误有时会表现为程序在最快优化出错而最小优化正常。把你认为可疑的变量加上 volatile 试试。

(3) 变量优化:优化程序会根据变量的使用情况优化变量。例如,函数中有一个未被使用的变量,在 Debug 版中它有可能掩盖一个数组越界,而在 Release 版中,这个变量很可能被优化调,此时数组越界会破坏栈中有用的数据。当然,实际的情况会比这复杂得多。与此有关的错误有:

 

  非法访问,包括数组越界、指针错误等。

例如

void fn(void)

{

int i;

i = 1;

int a[4];

{

int j;

j = 1;

}

a[-1] = 1;//当然错误不会这么明显,例如下标是变量 a[4] = 1;

}

j 虽然在数组越界时已出了作用域,但其空间并未收回,因而 i j 就会掩盖越界。而 Release 版由于 ij 并未其很大作用可能会被优化掉,从而使栈被破坏。

 

3. _DEBUG NDEBUG :当定义了 _DEBUG 时,assert() 函数会被编译,而 NDEBUG 时不被编译。除此之外,VC++中还有一系列断言宏。这包括:

ANSI C 断言 void assert(int expression );

C Runtime Lib 断言 _ASSERT( booleanExpression ); _ASSERTE( booleanExpression );

MFC 断言 ASSERT( booleanExpression );

VERIFY( booleanExpression );

ASSERT_VALID( pObject );

ASSERT_KINDOF( classname, pobject ); ATL 断言 ATLASSERT( booleanExpression );

此外,TRACE() 宏的编译也受 _DEBUG 控制。

所有这些断言都只在 Debug版中才被编译,而在 Release 版中被忽略。唯一的例外是 VERIFY() 。事实上,这些宏都是调用了 assert() 函数,只不过附加了一些与库有关的调试代码。如果你在这些宏中加入了任何程序代码,而不只是布尔表达式(例如赋值、能改变变量值的函数调用等),那么 Release 版都不会执行这些操作,从而造成错误。初学者很容易犯这类错误,查找的方法也很简单,因为这些宏都已在上面列出,只要利用 VC++ Find in Files 功能在工程所有文件中找到用这些宏的地方再一一检查即可。另外,有些高手可能还会加入 #ifdef _DEBUG 之类的条件编译,也要注意一下。

顺便值得一提的是 VERIFY() 宏,这个宏允许你将程序代码放在布尔表达式里。这个宏通常用来检查 Windows API 的返回值。有些人可能为这个原因而滥用VERIFY() ,事实上这是危险的,因为 VERIFY() 违反了断言的思想,不能使程序代码和调试代码完全分离,最终可能会带来很多麻烦。因此,专家们建议尽量少用这个宏。

 

4. /GZ 选项:这个选项会做以下这些事

(1) 初始化内存和变量。包括用 0xCC 初始化所有自动变量,0xCD ( Cleared Data ) 初始化堆中分配的内存(即动态分配的内存,例如 new ),0xDD ( Dead Data ) 填充已被释放的堆内存(例如 delete ),0xFD( deFencde Data ) 初始化受保护的内存(debug 版在动态分配内存的前后加入保护内存以防止越界访问),其中括号中的词是微软建议的助记词。这样做的好处是这些值都很大,作为指针是不可能的(而且 32 位系统中指针很少是奇数值,在有些系统中奇数的指针会产生运行时错误),作为数值也很少遇到,而且这些值也很容易辨认,因此这很有利于在 Debug 版中发现 Release 版才会遇到的错误。要特别注意的是,很多人认为编译器会用 0 来初始化变量,这是错误的(而且这样很不利于查找错误)。

(2) 通过函数指针调用函数时,会通过检查栈指针验证函数调用的匹配性。(防止原形不匹配)

(3) 函数返回前检查栈指针,确认未被修改。(防止越界访问和原形不匹配,与第二项合在一起可大致模拟帧指针省略 FPO 通常 /GZ 选项会造成 Debug 版出错而 Release 版正常的现象,因为 Release 版中未初始化的变量是随机的,这有可能使指针指向一个有效地址而掩盖了非法访问。除此之外,/Gm /GF 等选项造成错误的情况比较少,而且他们的效果显而易见,比较容易发现。

 

三、怎样"调试" Release 版的程序

遇到 Debug 成功但 Release 失败,显然是一件很沮丧的事,而且往往无从下手。如果你看了以上的分析,结合错误的具体表现,很快找出了错误,固然很好。但如果一时找不出,以下给出了一些在这种情况下的策略。 1. 前面已经提过,Debug Release 只是一组编译选项的差别,实际上并没有什么定义能区分二者。我们可以修改 Release 版的编译选项来缩小错误范围。如上所述,可以把 Release 的选项逐个注:那篇文章到此就完了,好像还有一些没了。

 

VC中当整个工程较大时,软件时常为出现在DEBUG状态下能运行而在RELEASE状态下无法运行的情况。由于开发者通常在DEBUG状态下开发软件,所以这种情况时常是在我们辛苦工作一两个月后,满怀信心的准备将软件发行时发生。为了避免无谓的损失,我们最好进行以下的检查:

1、时常测试软件的两种版本。

2、不要轻易将问题归结为DEBUG/RELEASE问题,除非你已经充分对两种版本进行了测试。

3、预处理的不同,也有可能引起这样的问题。出现问题的一种可能性是在不同版本的编译间定义了不同的预处理标记。请对你的DEBUG版本的软件试一下以下改动:"Project Setting(ALT-F7)" 中的C/C++项中设置目录(category)"General",并且改动"_DEBUG"定义为"NDEBUG". 设置目录为"Preprocessor"并且添加定义"_DEBUG"Undefined Symbols"输入框. 选择Rebuild ALL,重新编译. 如果经过编译的程序产生了问题,请对代码进行如下改动:ASSERT() 改为 VERIFY(). 找出定义在"#ifdef _DEBUG"中的代码,如果在RELEASE版本中需要这些代码请将他们移到定义外。查找TRACE(...)中代码,因为这些代码在RELEASE中也不被编译。所以请认真检查那些在RELEASE中需要的代码是否并没有被便宜。

4、变量的初始化所带来的不同,在不同的系统,或是在DEBUG/RELEASE版本间都存在这样的差异,所以请对变量进行初始化。

5、是否在编译时已经有了警告?请将警告级别设置为34,然后保证在编译时没有警告出现.

6、是否改动了资源文件.

7、此外对RELEASE版本的软件也可以进行调试,请做如下改动:"Project Settings" "C++/C " 项目下设置 "category" "General" 并且将"Debug Info"设置为 "Program Database". "Link"项目下选中"Generate Debug Info"检查框。 "Rebuild All" 如此做法会产生的一些限制:无法获得在MFC DLL中的变量的值。必须对该软件所使用的所有DLL工程都进行改动。另: MS BUGMS的一份技术文档中表明,在VC5中对于DLL"Maximize Speed"优化选项并未被完全支持,因此这将会引起内存错误并导致程序崩溃。

************************************************************************ * Build and Install HDF5 Applications with CMake * ************************************************************************ Notes: This short instruction is written for users who want to quickly build HDF5 applications using the CMake tools. Users can adapt these instructions for their own applications. For more information, see the "Minimum C Project Files for CMake" section. More information about using CMake can be found at the KitWare site, www.cmake.org. CMake uses the command line; however, the visual CMake tool is available for the configuration step. The steps are similar for all of the operating systems supported by CMake. NOTES: 1. Using CMake for building and using HDF5 is under active development. While we have attempted to provide error-free files, please understand that development with CMake has not been extensively tested outside of HDF. The CMake specific files may change before the next release. 2. CMake for HDF5 development should be usable on any system where CMake is supported. Please send us any comments on how CMake support can be improved on any system. 3. See the appendix at the bottom of this file for an example of using a ctest script for building and testing. See INSTALL_CMake.txt for more information. 4. See https://cmake.org/cmake/help/latest/command/find_package.html for more information on the CMake "Config Mode Search Procedure". ======================================================================== I. Preconditions ======================================================================== 1. We suggest you obtain the latest CMake for your platform from the Kitware web site. The HDF5 1.12.x product requires a minimum CMake version of 3.12. If you are using VS2019, the minimum version is 3.15. For VS2022, the minimum version is 3.21. 2. You have installed the HDF5 library built with CMake, by executing the HDF Install Utility (the *.msi file in the binary package for Windows or the *.sh on Linux). You can obtain pre-built binaries from The HDF Group's website at www.hdfgroup.org. 3. Set the HDF5_ROOT CMake variable, -DHDF5_ROOT=<install_path> or environment variable, set(ENV{HDF5_ROOT} "<install_path>") to the installed location of HDF5. On Windows: HDF5_ROOT=C:/Program Files/HDF_Group/HDF5/1.12.x/ On unix: HDF5_ROOT=<install root folder>/HDF_Group/HDF5/1.12.x/ If you are using shared libraries, you may need to add to the path environment variable. Set the path environment variable to the installed location of the library files for HDF5. On Windows (*.dll): PATH=%PATH%;C:/Program Files/HDF_Group/HDF5/1.12.x/bin On unix (*.so): LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<install root folder>/HDF_Group/HDF5/1.12.x/lib If you are using filter plugin libraries, you will need to set the HDF5_PLUGIN_PATH environment variable. On Windows: HDF5_PLUGIN_PATH=C:/Program Files/HDF_Group/HDF5/1.12.x/lib/plugin On unix: HDF5_PLUGIN_PATH=<install root folder>/HDF_Group/HDF5/1.12.x/lib/plugin (Note there are no quote characters used on Windows and all platforms use forward slashes) 4. Created separate source and build directories. (CMake commands are executed in the build directory) 5. Created a CMakeLists.txt file(s) for your source. See Section III below. ======================================================================== II. Building HDF5 Applications with CMake ======================================================================== Go through these steps to build HDF5 applications with CMake. (The application must support building with CMake.) 1. Run CMake 2. Configure the cache settings 3. Build HDF5 Applications 4. Test HDF5 Applications These steps are described in more detail below. 1. Run CMake The visual CMake executable is named "cmake-gui.exe" on Windows and should be available in your Start menu. For Linux, UNIX, and Mac users the executable is named "cmake-gui" and can be found where CMake was installed. Specify the source and build directories. Make the build and source directories different. For example on Windows, if the source is at c:\MyHDFstuff\hdf5, then use c:\MyHDFstuff\hdf5\build or c:\MyHDFstuff\build\hdf5 for the build directory. PREFERRED: Users can perform the configuration step without using the visual cmake-gui program. The following is an example command line configuration step executed within the build directory: cmake -G "<generator>" [-D<options>] <sourcepath> Where <generator> is * MinGW Makefiles * NMake Makefiles * Unix Makefiles * Visual Studio 14 2015 * Visual Studio 14 2015 Win64 * Visual Studio 15 2017 * Visual Studio 15 2017 Win64 * Visual Studio 16 2019 * ... in addition VS2019 will need to set the "-A" option, * ... [Win32, x64, ARM, ARM64] * Visual Studio 17 2022 * ... in addition VS2022 will need to set the "-A" option, * ... [Win32, x64, ARM, ARM64] <options> is: * BUILD_TESTING:BOOL=ON * BUILD_SHARED_LIBS:BOOL=[ON | OFF] 2. Configure the cache settings 2.1 Visual CMake users, click the Configure button. If this is the first time you are running cmake-gui in this directory, you will be prompted for the generator you wish to use (for example on Windows, Visual Studio 14 2015 Win64). CMake will read in the CMakeLists.txt files from the source directory and display options for the HDF5 project. After the first configure you can adjust the cache settings and/or specify locations of other programs. Any conflicts or new values will be highlighted by the configure process in red. Once you are happy with all the settings and there are no more values in red, click the Generate button to produce the appropriate build files. On Windows, if you are using a Visual Studio generator, the solution and project files will be created in the build folder. On linux, if you are using the Unix Makefiles generator, the Makefiles will be created in the build folder. 2.2 Alternative command line example on Windows in c:\MyHDFstuff\hdf5\build directory: cmake -G "Visual Studio 14 2015 Win64" -DBUILD_TESTING:BOOL=ON .. 3. Build HDF5 Applications On Windows, you can build HDF5 applications using either the Visual Studio Environment or the command line. The command line is normally used on linux, Unix, and Mac. To build from the command line, navigate to your build directory and execute the following: cmake --build . --config {Debug | Release} NOTE: "--config {Debug | Release}" may be optional on your platform. We recommend choosing either Debug or Release on Windows. If you are using the pre-built binaries from HDF, use Release. 3.1 If you wish to use the Visual Studio environment, open the solution file in your build directory. Be sure to select either Debug or Release and build the solution. 4. Test HDF5 Applications To test the build, navigate to your build directory and execute: ctest . -C {Debug | Release} NOTE: "-C {Debug | Release}" may be optional on your platform. We recommend choosing either Debug or Release to match the build step on Windows. 5. The files that support building with CMake are all of the files in the config/cmake folder, the CMakeLists.txt files in each source folder, and CTestConfig.cmake. CTestConfig.cmake is specific to the internal testing performed by The HDF Group. It should be altered for the user's installation and needs. The cacheinit.cmake file settings are used by The HDF Group for daily testing. It should be altered/ignored for the user's installation and needs. ======================================================================== III. Minimum C Project Files for CMake ======================================================================== Given the preconditions in section I, create a CMakeLists.txt file at the source root. Include the following text in the file: ########################################################## cmake_minimum_required (VERSION 3.12) project (HDF5MyApp C CXX) set (LIB_TYPE STATIC) # or SHARED string(TOLOWER ${LIB_TYPE} SEARCH_TYPE) find_package (HDF5 NAMES hdf5 COMPONENTS C ${SEARCH_TYPE}) # find_package (HDF5) # Find non-cmake built HDF5 set_directory_properties(PROPERTIES INCLUDE_DIRECTORIES "${HDF5_INCLUDE_DIR}") set (LINK_LIBS ${LINK_LIBS} ${HDF5_C_${LIB_TYPE}_LIBRARY}) set (example hdf_example) add_executable (${example} ${PROJECT_SOURCE_DIR}/${example}.c) TARGET_C_PROPERTIES (${example} PRIVATE ${LIB_TYPE}) target_link_libraries (${example} ${LINK_LIBS}) enable_testing () include (CTest) add_test (NAME test_example COMMAND ${example}) ########################################################## ======================================================================== IV. APPENDIX ======================================================================== Below is an example of a ctest script that can be used to build the examples. Adjust the values as necessary. Note that the defaults can be entered on the command line and the build folder is created as a sub-folder. Windows should adjust the forward slash to double backslashes, except for the HDF_DIR environment variable. NOTE: this file is available at the HDF web site: https://portal.hdfgroup.org/display/support/Building+HDF5+with+CMake HDF5_Examples.cmake HDF5_Examples_options.cmake Also available at the HDF web site is a CMake application framework template. You can quickly add files to the framework and execute the script to compile your application with an installed HDF5 binary. ======================================================================== For further assistance, send email to help@hdfgroup.org ========================================================================
最新发布
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值