一、断言:运行时与预处理时

断言(assertion)是一种编程常用的手段。想必大家都见过 assert 吧。今天我们就来了解一下它。

通常情况下,断言就是将一个返回值总是需要为真的判别式放在语句中,用于排除在设计的逻辑上不应该产生的情况。

比如一个函数总需要输入在一定的范围内的参数,那么程序员就额可以对该参数使用断言,以迫使在该参数发生异常的时候程序退出,从而避免程序陷入逻辑的混乱。

从一些意义上讲,断言并不是正常程序所必须的,不过对于程序调试来说,通常断言能够帮助程序开发者快速定位那些违反了某些前提条件的程序错误。

例子:

#include<cassert>
using namespace std; char* ArrayAlloc(int n)
{
assert(n > );
return new char[n];
} int main()
{
char* a = ArrayAlloc();
}

结果显示,很明显程序崩溃了。

这一点上,虽然异常可以防止程序崩溃,使程序得以继续执行,但是该结束程序的时候,还是要果断地用assert 结束程序

我们应该合理应用断言和异常。

在C++中,程序员也可以定义宏NDEBUG来禁用assert 宏。这对发布程序来说还是必要的。

因为程序用户退出总是敏感的,而且部分的程序错误也未必会导致程序全部功能失效。(比如上一段提到的异常就可以避免这种情况。)

那么通过定义NDEBUG宏发布程序也可以尽量避免程序退出的状况。而当程序有问题时,通过没有定义宏NDEBUG的版本,程序员则可以比较容易地找到出问题的位置。

事实上,assert 宏在<cassert>中的实现方式类似如下形式:

#ifdef NDEBUG
# define assert(expression) (static_cast<void>) //!!!!!
#else
...
#endif

可以看到,一旦定义了NDEBUG宏,assert 宏将被展开为一条无意义的C语句(通常被编译器优化掉)

但是有一个问题,定义NDEBUG宏的时候必须将其定义在相应头文件之前才行!!

在上一篇(三)中,我们还看到 #error 这样的预处理指令,而事实上,通过预处理指令#if 和#error 的配合,也可以让程序员在预处理阶段进行断言。

这样的用法也是极为常见的。

书上对于这方面是这样说的,原话如下:(由于没有相关的设备支持,所以后面自己写了个简单的工程测试了一下)

比如GNU的cmathcalls.h 头文件中(在我们的实验机上,该文件位于/user/include/bits/cmathcalls.h),我们会看到如下代码:
#ifndef _COMPLEX_h
#error "Never use <bits/cmathcalls.h> directly; include<complex.h>instead."
#endif
如果程序员直接包含头文件<bits/cmathcalls.h>并进行编译,就会引发错误。#error 指令会将后面的语句输出,从而提醒用户不要直接使用这个头文件,而应该包含头文件<complex.h>。

我们用这种方法测试一下:

我们首先写一个头文件  test.h 里面有一个类 _Test 文件定义宏为 _TEST

//test.h
#define _TEST class _Test
{
int m_int;
public:
_Test():m_int(){}
const int get_int()const { return m_int; }
};

我们再写另一个头文件 test1.h

//test1.h
#ifndef _TEST #error "请引用正确的头文件 test.h" #endif

意思是,如果该文件没有重定义宏 _TEST ,那么就用原版本

主调函数测试:

#include<iostream>
#include"test1.h"
using namespace std;
int main()
{
_Test a;
cout << a.get_int() << endl;
}

我们试图调用test1.h文件中的_TEST类,但是该文件没有重新定义该类

触发错误。

在次测试:

#include<iostream>
#include"test.h"
#include"test1.h"
using namespace std;
int main()
{
_Test a;
cout << a.get_int() << endl;
}

这样一来,通过预处理时的断言,库发布者就可以避免一些头文件的引用问题。

感谢您的阅读,生活愉快~

(四)静态断言(上),assert,NDEBUG, 以及通过宏定义处理文件包含关系的更多相关文章

  1. C 常用库函数memset,编译器宏定义assert

    一. 总览 1.1库函数 函数名 头文件 功能 原型 说明 syslog syslog.h 记录至系统记录(日志) void    syslog(int, const char *, ...) __p ...

  2. iOS 静态库中使用宏定义区分iPhone模拟器与真机---备用

    问题描述 一般项目中,可以使用宏定义来判断模拟器还是真机,这无疑是有效的. #if TARGET_IPHONE_SIMULATOR #define SIMULATOR 1 #elif TARGET_O ...

  3. Swift入门(四)——可选类型(Optionals)与断言(Assert)

    可选类型是什么? 首先看一个问题,Swift中String类型的变量有一个叫做toInt的方法,能够把String类型变量转换为Int类型变量. var stringValue = "5&q ...

  4. c++11 静态断言

    c++11 静态断言 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #includ ...

  5. C++断言与静态断言

    断言是很早之前就有的东西了,只需要引入cassert头文件即可使用.往往assert被用于检查不可能发生的行为,来确保开发者在调试阶段尽早发现“不可能”事件真的发生了,如果真的发生了,那么就表示代码的 ...

  6. C++11 静态断言(static_assert)

      简介 C++0x中引入了static_assert这个关键字,用来做编译期间的断言,因此叫做静态断言. 其语法很简单:static_assert(常量表达式,提示字符串). 如果第一个参数常量表达 ...

  7. python接口自动化(二十三)--unittest断言——上(详解)

    简介 在测试用例中,执行完测试用例后,最后一步是判断测试结果是 pass 还是 fail,自动化测试脚本里面一般把这种生成测试结果的方法称为断言(assert).用 unittest 组件测试用例的时 ...

  8. (五)静态断言(下),static_assert

    二.静态断言与static_assert 通过上一篇,我们可以看到,断言assert宏只有在程序运行的时候才能起作用.而#error值在编译器预处理时才能起作用. 有时候,我们希望在编译时候能做一些断 ...

  9. c语言静态断言

    在php中可以通过xdebug来显示详细的错误信息,可以细化到哪个文件哪行代码引起的报错.在C语言里面也可以通过静态断言(assert)来使得调试代码更加方便.关于断言,可以作为一种很强大的调试方式或 ...

随机推荐

  1. 【Linux】SecureCRT连接Linux乱码

    SecureCRT连接linux出现乱码问题.解决方法. 打开SecureCRT-->option-->Session option

  2. 【AtCoder Regular Contest 080E】Young Maids [堆][线段树]

    Young Maids Time Limit: 50 Sec  Memory Limit: 512 MB Description 给定一个排列,每次选出相邻的两个放在队头,要求字典序最小. Input ...

  3. Grunt、gulp、webpack、不要听着高大上你就上,试试Codekit?

    下载地址:https://incident57.com/codekit/ 官方网站了解更多 要编译Less.Sass.Stylus, CoffeeScript, Typescript, Jade, H ...

  4. Dream------Hadoop--HDFS的设计

    HDFS是为以流式数据访问模式存储超大文件而设计的文件系统.   流式数据访问 HDFS建立在这样一个思想上:一次写入.多次读取模式是最高效的.一个数据集通常由数据源生成或复制, 接着在此基础上进行各 ...

  5. python进阶之类常用魔法方法和魔法属性

    前言 前面我们总结过了python的关键字.运算符.内置函数.语法糖等与python魔法方法之间的关系,现在我们更细一点,看看python的面向对象编程有哪些常用的魔法属性和魔法方法. 魔法属性 对于 ...

  6. Shell-脚本只能运行1次

    用空文件进行判断 path=`pwd` if [ -f ${path}/.runned ]; then { echo "This script can only execute once! ...

  7. c++ 类的构造顺序

    在单继承的情况下,父类构造先于子类,子类析构先于父类,例: class A { public: A() { cout << "A" << endl; } ~ ...

  8. Python爬取微信好友

    前言 今天看到一篇好玩的文章,可以实现微信的内容爬取和聊天机器人的制作,所以尝试着实现一遍,本文记录了实现过程和一些探索的内容 来源: 痴海 链接: https://mp.weixin.qq.com/ ...

  9. Linux下实现多播(组播)

    单播只能发送给一个接收方,但是当给多个接收者发送时,不仅仅耗费流量,而且耗费时间,总流量=每个接收者的流量*接受者. 广播方式是发送给所有的主机,广播的坏处是会造成信息污染,大量的信息会占用网络带宽. ...

  10. Java容器---Set: HashSet & TreeSet & LinkedHashSet

    1.Set接口概述        Set 不保存重复的元素(如何判断元素相同呢?).如果你试图将相同对象的多个实例添加到Set中,那么它就会阻止这种重复现象. Set中最常被使用的是测试归属性,你可以 ...