首页 > 编程语言 >【编程】C++ 中逻辑与运算符 `&&` 具有短路求值的特性在assert中的应用

【编程】C++ 中逻辑与运算符 `&&` 具有短路求值的特性在assert中的应用

时间:2024-12-04 21:59:58浏览次数:14  
标签:__ assert 运算符 字符串 num && 求值 表达式

在这里插入图片描述

关于 assert 在 C++ 中使用 条件 && 字符串 格式的示例以及对其宏定义解析的相关说明:

1. assert 基本介绍及示例使用

在 C++ 中,assert 是一个宏定义,它位于 <cassert> 头文件(在 C 中是 <assert.h> )中,用于在程序开发阶段进行调试检查。它的基本语法形式是 assert(表达式),当 表达式 的值为 false(在 C++ 中也就是值为 0 )时,程序会终止执行,并输出相应的错误信息,这个错误信息通常包含了所在的文件名、行号以及你可以自定义的一些提示内容等。

使用 条件 && 字符串 格式的示例如下:

#include <cassert>
#include <iostream>

int main() {
    int num = 10;
    // 这里使用条件 && 字符串的形式,条件是 num 大于 5,并且后面跟着自定义的提示字符串
    assert(num > 5 && "输入的数字应该大于5");
    std::cout << "程序正常执行到这里,num 的值为: " << num << std::endl;
    return 0;
}

在上述代码中,num 的值是 10num > 5 这个条件为 true,所以 assert 宏不会触发错误,程序会正常执行并输出后面的语句。

如果我们修改 num 的值为 3,像这样:

#include <cassert>
#include <iostream>

int main() {
    int num = 3;
    assert(num > 5 && "输入的数字应该大于5");
    std::cout << "程序正常执行到这里,num 的值为: " << num << std::endl;
    return 0;
}

此时,num > 5 这个条件为 false,那么 assert 宏就会起作用,程序会终止执行,并输出类似下面这样的错误信息(不同编译器输出格式可能略有差异):

Assertion failed: (num > 5 && "输入的数字应该大于5"), function main, file main.cpp, line 7.

可以看到它包含了我们自定义的字符串 "输入的数字应该大于5",方便我们在调试时知道具体是哪个条件不符合预期了。

2. 对 assert 宏定义的解析

在标准 C++ 库中(以常见的实现方式为例),assert 宏的定义大致类似如下(实际可能因编译器和标准库实现有细微不同):

#ifdef NDEBUG
#define assert(condition) ((void)0)
#else
#define assert(condition) \
    ((condition)? (void)0 : __assert_fail(#condition, __FILE__, __LINE__, __func__))
#endif

可以看到有这样的逻辑:

  • 首先,有一个条件编译判断 #ifdef NDEBUGNDEBUG 是一个预定义的宏,如果在编译时定义了这个宏(通常在发布版本中会这么做,以去除所有的 assert 检查,提高程序执行效率),那么 assert(condition) 就被定义为 ((void)0),也就是什么都不做,相当于 assert 宏被“禁用”了。
  • 而在没有定义 NDEBUG 的情况下(一般是在调试阶段),assert 宏展开后的逻辑是这样的:它首先判断 (condition),如果这个 conditiontrue(也就是非 0 值),就执行 (void)0,也就是不做任何额外操作,程序继续正常往下执行;但如果 conditionfalse(值为 0 ),就会调用 __assert_fail 函数。这个 __assert_fail 函数是由标准库提供的,它内部会输出错误信息,包括传入的 #condition(这里 # 操作符会把 condition 这个表达式转换为对应的字符串形式,比如对于 num > 5 就会转换为 "num > 5" ),还有当前所在的文件名(__FILE__ 宏提供)、行号(__LINE__ 宏提供)以及函数名(__func__ 宏提供,如果有的话),然后终止程序的执行。

例如在前面 num = 3 的那个错误示例中,当 num > 5 这个条件为 false 时,就会调用 __assert_fail 函数,传入的参数就是 "num > 5"#condition 的转换结果)、"main.cpp"__FILE__ 宏的值,当前文件名)、对应的行号以及函数名等,最后输出完整的错误提示信息来帮助我们定位问题所在。

所以总体来说,assert 宏结合 条件 && 字符串 这种形式,通过巧妙的宏定义和条件编译逻辑,为我们在程序调试阶段提供了一种方便的、能快速定位不符合预期条件情况的手段,同时在发布版本中又可以通过定义 NDEBUG 来去除相关的检查,避免额外的运行时开销。

3.进一步解析短路求值

在 C 和 C++ 中,逻辑与运算符 && 具有短路求值的特性。

当使用 条件 && 字符串 这样的表达式作为 assert 的参数时(其实在任何使用逻辑与的地方都是如此逻辑):

条件为 true 时的情况

如果 条件 的计算结果为 true(也就是值为非零),根据逻辑与运算符 && 的运算规则,它还需要继续判断右侧表达式(也就是这里的 字符串 表达式)的值来确定整个逻辑与表达式最终的真假情况。此时,字符串会被当作一个指针来处理,在 C/C++ 中,非空字符串常量的指针值是不为 0 的(字符串有其有效的内存地址存储其字符内容),所以右侧的表达式值也为 true,进而整个 条件 && 字符串 表达式的结果就为 trueassert 宏也就不会触发错误提示,程序继续正常执行。

例如以下代码片段:

#include <iostream>

int main() {
    int num = 10;
    bool result = num > 5 && "This is a valid string";
    std::cout << "The result of the expression: " << (result? "true" : "false") << std::endl;
    return 0;
}

这里 num > 5true,然后会去评估 "This is a valid string",它作为一个非空字符串常量指针,其值被视为 true,所以最终 result 变量被赋值为 true,程序会正常输出相应内容,表示整个逻辑与表达式结果为 true

条件为 false 时的情况

条件 的计算结果为 false(值为 0 ),基于逻辑与运算符 && 的短路求值特性,它一旦发现左侧表达式为 false,就已经能确定整个逻辑与表达式的结果必然为 false 了,所以就不会再去计算右侧的 字符串 表达式的值了,直接判定整个 条件 && 字符串 表达式为 false

比如以下代码:

#include <iostream>

int main() {
    int num = 3;
    bool result = num > 5 && "This string won't be evaluated";
    std::cout << "The result of the expression: " << (result? "true" : "false") << std::endl;
    return 0;
}

在这个例子中,num > 5false,因此按照逻辑与的短路求值,根本不会去管右侧的 "This string won't be evaluated" 这个字符串表达式的值,就直接判定整个逻辑与表达式的值为 false,程序继续往下执行相应后续语句,只是 result 的值会被赋值为 false 而已。

这种短路求值特性在很多场景下都很有用,除了像 assert 宏里用于在条件不满足时避免不必要的额外计算外,还常用于一些逻辑判断中,避免因不必要地计算复杂的右侧表达式而可能引发的潜在错误(比如右侧表达式里包含函数调用,如果每次都调用可能会有副作用等情况)。

标签:__,assert,运算符,字符串,num,&&,求值,表达式
From: https://blog.csdn.net/gzjimzhou/article/details/144225610

相关文章

  • Java入门--运算符和表达式
    Java入门1、算术运算符实现对两个数的运算,程序输出效果为:请输入第一个整数:25请输入第二个整数:8两数相加的结果为:33两数相减的结果为:17两数相乘的结果为:200两数相除的结果为:3两数取余数的结果为:1以下是Java代码:publicclassCalculate{publicstaticvoidmain(......
  • SQL-基础语法 - 条件查询 - 运算符
    运算符是SQL中用于在条件查询中进行条件判断的特殊符号,比如=、!=、<、>等。通过使用不同的运算符,我们可以在查询语句中设定多样化的条件,从而根据数据的不同属性进行灵活的筛选和过滤。假设你是一名招聘官,而数据表中的数据就像是你面试的候选人。不同的运算符就像是你设定的......
  • 位运算符
    位运算符在Java中,|=是一个位运算符,称为按位或赋值运算符。它的作用是将左侧变量与右侧表达式进行按位或(OR)操作,并将结果赋值给左侧变量。对于config|=system;这行代码,它的意思是:config是一个整数变量,用来存储当前的配置状态。system是一个整数,代表要使能的系统,它的二进......
  • 一不小心就容易出错的c语言运算符优先级
      有些人说c语言是简洁高效的,又有些人说c语言是深邃复杂的,说实话,这确实是仁者见仁智者见智。但是有一点不可否认,c语言中的运算符众多,不注意的话,确实很容易弄错。一、*与.的优先级比较  对于一个结构体p包含一个指针*f,那么*p.f的运算优先规则是怎样?  *p.f=*(p......
  • 逻辑运算符
    逻辑运算符在程序开发中,通常在判断条件时,需要同时判断多个条件,只有多个条件都满足,才能执行后续代码,这个时候需要用到逻辑运算符运算符逻辑表达式描述实例andxandy布尔“与”:如果x为False,xandy返回False,否则它返回y的值。TrueandFalse,返回False。or......
  • JavaScript 运算符
    JavaScript 运算符运算符=用于赋值。运算符+用于加值。运算符=用于给JavaScript变量赋值。算术运算符 + 用于把值加起来。实例指定变量值,并将值相加:y=5;z=2;x=y+z;在以上语句执行后,x 的值是:7尝试一下»JavaScript算术运算符与/或值之间的算术......
  • 这两种展开运算符的方式有什么区别呢?
    在JavaScript的前端开发中,有两种主要的展开运算符(spreadoperator)...的用法,它们分别应用于数组/类数组和对象。虽然符号相同,但作用略有不同:1.数组/类数组的展开:作用:将数组或类数组的元素"展开"成独立的项。场景:复制数组:创建一个新数组,包含原数组所有元素......
  • C#面向对象之抽象,接口,运算符重载,属性,索引器
    目录1抽象1.1抽象方法1.1.1抽象方法1.1.2虚方法1.1.3new1.2抽象属性1.3抽象示例2接口2.1定义2.2简单使用2.2.1声明使用接口2.2.2接口继承2.3接口显式实现和隐式实现2.3.1隐式实现2.3.2显式实现2.3.3多接口实现中的应用3运算符重载operator3.1简介3.2示例4属......
  • 【C++】数据的输入、运算符(上)
    1.数据的输入作用:用于从键盘上获取数据关键字:cin语法:cin>>变量;例如:代码:#include<iostream>usingnamespace std;intmain(){   //整形数据   inta=0;   cout<<"请给整型变量a赋值:"<<endl;   cin>>a;   cout<<"整型变量a="......
  • Python入门基础语法之运算符类型和语法
    运算符类型和语法运算符基本概念需注意,虽然python中无常量,但人为规定,在运行过程中值不能改变的为常量,要全部大写。操作数:参与运算的变量或者常量或具体的数值操作符:将参与运算的量连接起来的符号表达式:由操作数和操作符构成且符合python语法规范的式子,不同的操作符可以......