Windows下断言的类型及实现

一、内容综述

本文主要介绍Windows下断言assert的实现,并总结断言的不同应用准则。最后给出一个windows自定义断言的方法。

本文行文参考《Debugging Windows Programs》第三章相关内容,如果有兴趣的话建议读者可以深入阅读下。

 

二、断言的类型

 1.  ANSI C 断言

 ANSI C断言指的是assert函数,按照标准定义,assert是支持跨平台的,原型如下:

void assert (int expression);

C语言标准中规定,编译器实现的断言失败消息至少包含以下信息:

Assertion failed: expression(断言失败的参数), file filename(源文件名称), line line number(断言失败的行号)。

另外,C语言标准中支持,在包含<assert.h>或<cassert>头文件之前定义NDEBUG宏,可禁用assert函数的断言判断。

更加详细的信息可参考:http://www.cplusplus.com/reference/cassert/assert/

Windows下assert函数,在控制台程序和基于Windows的程序下弹窗差别较大。比如在我的pc下(Win7,vs2010,debug编译),控制台程序使用stderr显示断言消息,并弹出Debug Error的窗口。

 而在MFC中弹窗如下:

 二者断言失败的表现不太相同,但均符合C语言标准的规定。

从上面两个截图也可以看出,当源文件路径比较长时,assert断言的消息提示会把文件路径截断为短路径格式,这样很不容易定位出问题的代码。因此,在Windows下不推荐直接使用assert断言函数。

其他更详细的assert信息,可参考:http://msdn.microsoft.com/en-us/library/9sb57dw4.aspx。

如下代码给出了,vs2010中assert函数的头文件声明(assert.h头文件)

#include <crtdefs.h>#undef  assert#ifdef  NDEBUG#define assert(_Expression)     ((void)0)#else#ifdef  __cplusplus
extern "C" {
#endif_CRTIMP void __cdecl _wassert(_In_z_ const wchar_t * _Message, _In_z_ const wchar_t *_File, _In_ unsigned _Line);#ifdef  __cplusplus
}
#endif#define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )#endif  /* NDEBUG */
View Code

从中也可以看出NDEBUG宏为什么可以影响assert断言了。

2.  C-Runtime Library断言

 C Runtime Library(CRT)提供了_ASSERT、_ASSERTE两种形式的断言,位于<crtdbg.h>头文件中。_ASSERT、_ASSERTE函数仅在_DEBUG宏定义的情况下有效,这也是vs的debug版本中预定义宏_DEBUG的作用。

另外,这两个函数在控制台程序和基于Windows的应用程序下断言失败的提示消息框是一样的。

_ASSERT会弹出如下所示的断言失败提示框

 _ASSERT断言失败提示框如下:

 

对比两个对话框,_ASSERTE相比_ASSERT多了关于断言失败表达式的信息。这样可以在不查看源代码的情况下分析断言失败的原因,但是过多调用_ASSERTE会使debug版本的可执行程序中保存大量关于断言失败的表达式字符串,文件较大。

下面代码是vs2010中crtdbg.h头文件给出的关于_ASSERTE、_ASSERT的声明,可以参考下

 

// 摘录版本,只给出_ASSERT、_ASSERTE的相关部分
#ifndef _DEBUG
/*  We allow our basic _ASSERT macros to be overridden by pre-existing definitions. This is not the ideal mechanism, but is helpful in some scenarios and helps avoidmultiple definition problems */
#ifndef _ASSERT
#define _ASSERT(expr) ((void)0)
#endif #ifndef _ASSERTE
#define _ASSERTE(expr) ((void)0)
#endif#else
/* Asserts */
/* We use !! below to ensure that any overloaded operators used to evaluate expr do not end up at operator || */
#define _ASSERT_EXPR(expr, msg) \(void) ((!!(expr)) || \(1 != _CrtDbgReportW(_CRT_ASSERT, _CRT_WIDE(__FILE__), __LINE__, NULL, msg)) || \(_CrtDbgBreak(), 0))#ifndef _ASSERT
#define _ASSERT(expr)   _ASSERT_EXPR((expr), NULL)
#endif#ifndef _ASSERTE
#define _ASSERTE(expr)  _ASSERT_EXPR((expr), _CRT_WIDE(#expr))
#endif#if     !defined(_CRT_PORTABLE)
#define _CrtDbgBreak() __debugbreak()
#else
_CRTIMP void __cdecl _CrtDbgBreak(void);
#endif#endif
View Code

 

如果有兴趣深入了解,可以参考:http://msdn.microsoft.com/en-us/library/ezb1wyez.aspx。

3. MFC断言

 MFC中,我用的最多的是VERIFY、ASSERT两个断言。

ASSERT和VERIFY断言失败的弹窗和_ASSERT相关,首先看下ASSERT、VERIFY的实现。代码来自vs2010的相关头文件

// from afxassert.cpp
#ifdef _DEBUG   // entire file// NOTE: in separate module so it can replaced if needed

BOOL AFXAPI AfxAssertFailedLine(LPCSTR lpszFileName, int nLine)
{
#ifndef _AFX_NO_DEBUG_CRT// we remove WM_QUIT because if it is in the queue then the message box// won't display
    MSG msg;BOOL bQuit = PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);BOOL bResult = _CrtDbgReport(_CRT_ASSERT, lpszFileName, nLine, NULL, NULL);if (bQuit)PostQuitMessage((int)msg.wParam);return bResult;
#else// Not supported.
#error _AFX_NO_DEBUG_CRT is not supported.
#endif // _AFX_NO_DEBUG_CRT
}
#endif // _DEBUG// from afxver_.h
#ifndef AfxDebugBreak
#define AfxDebugBreak() __debugbreak()
#endif#ifndef _DEBUG
#ifdef AfxDebugBreak
#undef AfxDebugBreak
#endif
#define AfxDebugBreak()
#endif  // _DEBUG// from afx.h
#ifdef _DEBUG
BOOL AFXAPI AfxAssertFailedLine(LPCSTR lpszFileName, int nLine);#define THIS_FILE          __FILE__
#define VERIFY(f)          ASSERT(f)
#define DEBUG_ONLY(f)      (f)#else   // _DEBUG#define VERIFY(f)          ((void)(f))
#define DEBUG_ONLY(f)      ((void)0)#endif // !_DEBUG#define ASSERT(f)          DEBUG_ONLY((void) ((f) || !::AfxAssertFailedLine(THIS_FILE, __LINE__) || (AfxDebugBreak(), 0)))
View Code

最终调用__debugbreak(编译器内部提供的函数)。

从上面的源码中也可看出ASSERT和VERIFY的区别,_DEBUG宏定义的情况下二者完全功能和实现一样;_DEBUG宏未定义的情况下,ASSERT将直接忽略断言条件,而VERIFY会执行以下断言条件语句。需要说明的是VERIFY也是断言的一种,在Release模式下(通常_DEBUG宏不会定义)断言是失效的,所以不推荐使用VERIFY用于判断可能发生的错误,更进一步不推荐使用VERIFY,VERIFY对防御性编程有过多的误导作用。

三、自定义断言

 如果windows提供的断言不能够或者不适合与实际需要,可以自定义断言的形式来提供更加自由的错误跟踪体系,通常自定义断言出于以下考虑

  • 不同类型的程序提供可移植性,比如mfc、console、windows api、跨平台需要。
  • 简化事后调试及问题跟踪,在错误发生时提供调用堆栈。
  • 不同的错误处理策略,比如不想看到系统弹出错误提示,而换用其他异常处理逻辑。

当然,也可能有其他要求。但是这里仅提供一种自定义断言的方法,至于是否合适、是否需要重定义断言还是需要读者自己决定。通过上面第二部分关于断言的介绍,显然自己写断言处理并不复杂,可使用_CrtDbgReport和_CrtDgbBreak即可完成多数断言工作。比如类似如下代码的断言实现:

// 多字节版本,使用Unicode需要修改调用函数
#ifndef _DEBUG
#define VASSERT(expr, expr_desc) ((void)0)
#else
#define VASSERT(expr, expr_desc) \do { if (!(expr) && \(1 == _CrtDbgReport(_CRT_ASSERT, (__FILE__), __LINE__, \NULL, #expr##"\nProblem: "##expr_desc))) \_CrtDbgBreak();}while(0)
#endif

仅供参考的代码,功能相对比较简单。

 

四、断言使用准则(推荐)

 1. 什么情况下需要使用断言?

断言仅用于检查有效性,而不是正确性。也就是说即使程序有错误,断言要做的是发现错误,在错误经过断言时能够报告程序中有错误,而不是报告程序不正确。断言是给开发人员使用的代码诊断的有效工具。断言通常用于以下几种情况:

  • 检查函数输入(前置条件)。验证函数参数、成员变量、全局变量,以及其他可能使用的变量状态。
  • 检查函数输出(后置条件)。特别是在经过特殊而复杂的处理逻辑之后,检查输出参数、对象成员的有效性。
  • 检查对象当前状态。比如对象是否被正常初始化、当前状态是否允许指定函数调用或者输入。
  • 检查逻辑变量的合理性和一致性。包括循环不变量、计数器、偏移变量、不可能出现的值、不可能出现的情况以及某些固定的关系等。
  • 检查类的不变量。

2. 什么情况下不能使用断言?

  • 断言不能够检查哪些可能正确也可能错误的情况。正确运行的程序不会使任何断言生效,如果发生断言失效的情况,则程序一定存在某种错误。
  • 断言不是防御性编程的替代品。断言不能防止程序的发布版本崩溃。
  • 断言不能检查哪些不是实现方面的错误。这些非实现方面的错误包括用户输入错误、资源分配错误、文件系统错误以及硬件错误。断言不是异常处理、返回值或其他形式的错误处理的替代品。
  • 断言不能够向用户报告错误。
  • 断言不能不能包含程序代码,不能有副作用。断言不能修改变量,也不能调用修改程序变量的函数。

 

转载于:https://www.cnblogs.com/tocy/p/4098245.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux右上角不显示网络连接_来体验下Linux吧

在前面的几期中我们从树莓派开始了解Linux&#xff0c;大家可能已经想来试一下手了。趁热打铁&#xff0c;本期我将介绍两种方便体验学习Linux的方法&#xff0c;在线体验或者安装虚拟机。1 在线体验Linux如果想快速的体验下Linux系统&#xff0c;我们可以选择云计算服务商提供…

python md5解密_python 生成文件MD5码

pymd5.py的代码如下&#xff1a; #-*-coding:utf-8-*-Created on 2012-5-25 author: kanpiaoxueimport hashlib import os import sys def printUsage(): print (Usage: [python] pymd5.py ) def createMD5(filePath): if not os.path.isfile(filePath): printUsage() else: tm…

Java数据结构、list集合、ArrayList集合、LinkedList集合、Vector集合

数据结构&#xff1a; 数据存储的常用结构有&#xff1a;栈、队列、数组、链表、红黑树。 栈&#xff1a;stack,又称堆栈&#xff0c;它是运算受限的线性表&#xff0c;其限制是仅允许在标的一端进行插入和删除操作&#xff0c;不允许在其他任何位置进行添加、查找、删除等操…

第11章 GUI Page436 使用缓冲DC, wxBufferedPaintDC

所谓“缓冲DC”&#xff0c;是指将所有图元都先划到一个人眼看不到的“设备上下文”之上&#xff0c;最后再一次性复制到真正的屏幕DC之上&#xff0c;这样我们就看不到中间画的过程了&#xff0c;也就不会感到闪烁了。 注意&#xff0c;这时不能解除ScrolledWindow1的背景擦除…

Tomcat+JSP经典配置实例

经常看到jsp的初学者问tomcat下如何配置jsp、servlet和bean的问题&#xff0c;于是总结了一下如何tomcat下配置jsp、servlet和ben&#xff0c;希望对那些初学者有所帮助。    一、开发环境配置    第一步&#xff1a;下载j2sdk和tomcat&#xff1a;到sun官方站&#xf…

python海龟编辑器画小汽车_【海龟编辑器下载】海龟编辑器(Python编辑器) v1.3.4 官方免费版-趣致软件园...

海龟编辑器是一款专为广大少儿打造的Python编辑器&#xff0c;该软件功能强大且使用便利&#xff0c;可以帮助孩子们以搭积木的方式来学习Python&#xff0c;并且支持Python代码和图形化积木的双向互相转译&#xff0c;不但可以有效提升孩子的学习兴趣&#xff0c;而且还能够降…

微信小程序中组件的使用

微信小程序中组件的使用&#xff1a; 微信小程序中组件定义在项目根目录下components文件夹下&#xff0c;在页面使用的时候需要在页面的json文件中声明&#xff0c;如 父组件向子组件传值及子组件向父组件传值&#xff1a; 父组件wxml中&#xff1a;通过属性绑定值的方式向…

如何保证交叉表编译器和目标系统版本一致_嵌入式系统词汇表

欢迎FPGA工程师加入官方微信技术群点击蓝字关注我们FPGA之家-中国最好最大的FPGA纯工程师社群AASIC(专用集成电路) Application-Specific Integrated Circuit. A piece of custom-designed hardware in a chip. 专用集成电路。一个在一个芯片上定制设计的硬件。address bus (地…

动态定义数组

首先&#xff1a;为什么需要动态定义数组呢&#xff1f; 这是因为&#xff0c;很多情况下&#xff0c;在预编译过程阶段&#xff0c;数组的长度是不能预先知道的&#xff0c;必须在程序运行时动态的给出 但是问题是&#xff0c;c要求定义数组时&#xff0c;必须明确给定数组的…

sql随机抽取数据50条_厉害!苏宁通过citus打造分布式数据库抗住DB高负载

2018-07-16 23:59内容来源&#xff1a;2017 年 10 月 20 日&#xff0c;苏宁云商IT总部资深技术经理陈华军在“PostgreSQL 2017中国技术大会”进行《苏宁citus分布式数据库应用实践》演讲分享。IT 大咖说&#xff08;微信id&#xff1a;itdakashuo&#xff09;作为独家视频合作…

java中Debug调试、异常的概念、异常体系、处理异常的关键字try/catch/finally/throw/throws、多个异常使用捕获并处理的方式、继承关系中处理异常、自定义异常类

Debug调试&#xff1a; bug指程序当中遇到的一些错误异常&#xff0c;Debug指调试bug,找到bug对其解决&#xff1b;debug可以让代码逐行执行&#xff0c;查看代码执行的过程&#xff0c;调试程序中出现的bug或异常。 public class DebugClass {public static void main(Strin…

densenet网络结构_FC-DENSENET用于图像分割学习笔记

近日使用FC Dense Net 做分割&#xff0c;记录学习使用过程。FC-DenseNet 原文链接&#xff1a;https://arxiv.org/abs/1611.09326 《The One Hundred Layers Tiramisu: Fully Convolutional DenseNets for Semantic Segmentation》初次使用此网络&#xff0c;第一眼是被这个名…

mongodb mysql资源占用_如何限制mongodb启动时占用过多内存

默认情况下&#xff0c;mongodb占用的内存大小为&#xff1a;Starting in 3.4, the WiredTiger internal cache, by default, will use the larger of either:50% of RAM minus 1 GB, or256 MB.下面我们来看一下限制mongodb启动时占用内存的方法&#xff1a;新增配置文件 /etc/…

js ...运算符_「 giao-js 」用js写一个js解释器

前言在这篇文章中,我们将通过 JS 构建我们自己的 JS 解释器,用 JS 写 JS,这听起来很奇怪,尽管如此,这样做我们将更熟悉 JS,也可以学习 JS 引擎是如何工作的!什么是解释器 (Interpreter) ?解释器是在运行时运行的语言求值器,它动态地执行程序的源代码。 解释器解析源代码,从源代…

地区json文件_【小例子】使用jQuery实现省市区三级联动显示,附源码json文件

开发工具-intellij IDEA需求1.实现对json文件的读取。2.可以在省级选择所有省名和直辖市名3.选择一级省名后自动刷新市名4.选择二级市名后自动刷新区名逻辑分析第一步&#xff1a;读取json文件第二步&#xff1a;遍历出所有一级的省名第三步&#xff1a;把遍历出来的省名动态追…

qt 状态栏

有段时间没有写过博客了。假期去上海旅游&#xff0c;所以一直没有能够上网。现在又来到这里&#xff0c;开始新的篇章吧&#xff01;今天的内容主要还是继续完善前面的那个程序。我们要为我们的程序加上一个状态栏。状态栏位于主窗口的最下方&#xff0c;提供一个显示工具提示…

动态获取textarea后面的p标签_HTML简单标签连起实现的小玩意:

《今天不发知识点&#xff0c;刚被误封了&#xff0c;所有没有太多时间去给整理哈&#xff0c;请谅解》&#xff08;谢谢欣赏&#xff09;前面发了那么多HTML标签&#xff0c;今天来玩个小视频&#xff0c;小白也能几分钟就会的&#xff0c;代码没有写好&#xff0c;时间急了点…

elipse+pydev+python开发arcgis脚本程序

环境配置参考&#xff1a;http://www.cnblogs.com/halfacre/archive/2012/07/22/2603848.html 添加arcpy类库、arctoolbox、arcgis-bin如下所示。 windos——preference——pydev 完成环境配置。 二、获取文件路径Python方法 os.getcwd()返回的是当前文件的目录。假如我的代码…

BZOJ3427 Poi2013 Bytecomputer

可以YY一下嘛 最后一定是-1, -1, ..., -1, 0, 0, ... 0, 1, 1, ..., 1的一个数列 于是f[i][j]表示到了第i个数&#xff0c;新数列的第j项为-1 or 0 or 1的的最小代价 然后就没有然后了 1 /**************************************************************2 Problem: 3427…

mysql timdir_MYSQL学习笔记——数据类型

mysql的数据类型可以分为三大类&#xff0c;分别是数值数据类型、字符串数据类型以及日期时间数据类型。数值数据类型数值类型又可以分为整型、浮点类型、Decimal。整型mysql的整型可以分为TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT&#xff0c;下表给出了每个类型的存储空间…