猿创征文丨学C/C++进来看看--你可能都不清楚的三种变量和零值比较

2023-11-06 18:59

本文主要是介绍猿创征文丨学C/C++进来看看--你可能都不清楚的三种变量和零值比较,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文主要讲解了:什么是bool类型,如何使用bool类型,bool类型如何和零值比较,浮点型变量如何和零值比较,三种常见的零值类型,指针变量和零值比较.

在这里插入图片描述

介绍三种变量以及如何和零值比较

  • 一.bool变量和零值比较
    • 1.bool类型介绍
    • 2.bool类型使用
    • 3.bool类型如何和零值比较
  • 二.浮点型变量和零值比较
    • 1.浮点数在内存中可能存在精度损失
    • 2.浮点数变量如何和零值比较
  • 三.指针变量与零值比较
    • 1.常见的零值
    • 2.指针变量如何和零值比较?
  • 四.总结

一.bool变量和零值比较

1.bool类型介绍

BOOL. bool表示布尔型变量,也就是逻辑型变量的定义符,以英国数学家、 布尔代数 的奠基人 乔治·布尔 (George Boole)命名。

C语言有没有bool类型?
c99之前,主要是c90是没有的,目前大部分书,都是认为没有的。因为书,一般都要落后于行业。
但是c99引入了_Bool类型(你没有看错,_Bool就是一个类型,不过在新增头文件stdbool.h中,被重新用宏写成了
bool,为了保证C/C++兼容性)。

2.bool类型使用

//测试代码1
#include <stdio.h>
#include <stdbool.h> //没有这个头文件会报错,使用新特性一定要加上
#include <windows.h>
int main()
{
bool ret = false;
ret = true;
printf("%d\n", sizeof(ret)); //vs2013 和 Linux中都是1
system("pause");
return 0;
}

上面代码(是c99标准下编译)可以得出bool类型占字节长度是1个字节
在c99里bool是一个新关键字,可以理解为和int 等一样,但是它类型长度是1个字节
它定义的变量只能赋值为true或false
true表示真,和常量1等价,false表示假和常量0等价,虽然等价但是用这两个表示更具有直观性,

但是!!!
因为目前编译器对C99特性支持的并不全面,我们后面依旧默认使用C90的认识去编码即可,使用int表示真假。
具体要结合实际情况去定。

如果想要类似的效果,可以使用#define true 1 #define false 0
#define定义标识符常量 方法 用这两个表示真假
但是不同的是类型整形大小是4个字节

3.bool类型如何和零值比较

int main()
{
int pass = 0; //0表示假,C90,我们习惯用int表示bool
//bool pass = false; //C99
if (pass == 0){ //理论上可行,但此时的pass是应该被当做bool看待的,==用来进行整数比较,不推荐
//TODO
} i
f (pass == false){ //不推荐,尽管在C99中也可行
//TODO
} i
f (pass){ //推荐
//TODO
} /
/理论上可行,但此时的pass是应该被当做bool看待的,==用来进行整数比较,不推荐
//另外,非0为真,但是非0有多个,这里也不一定是完全正确的
if (pass != 1){
//TODO
} i
f (pass != true){ //不推荐,尽管在C99中也可行
//TODO
} i
f (!pass){ //推荐
//TODO
} s
ystem("pause");
return 0;
}

上面代码讲解了bool多个和零值比较的方法
在c99中推荐:直接用bool变量作为真和假的判断
在c90中因为没有bool类型,但是也有非零为真,零为假的表示方法
推荐直接用变量作为真假的判断如(if(1) if(0)),√
不推荐再和特定值进行运算最后的结果作为真假判断(如:if(0==0))×
结论:bool类型,直接判定,不用操作符进行和特定值比较。(这样更直观的能看出表达式是真还是假.)

二.浮点型变量和零值比较

在这篇博客中详细介绍到了浮点型变量->浮点型变量
在上面博客介绍中:浮点数在内存中存储,并不想我们想的,是完整存储的,在十进制转化成为二进制,是有可能有精度损失的。
//注意这里的损失,不是一味的减少了,还有可能增多。浮点数本身存储的时候,在计算不尽的时候,会“四舍五入”或者其他策略

1.浮点数在内存中可能存在精度损失

#include <stdio.h>
int main()
{double x = 3.6;//浮点数3.6 存在内存里会发生精度损失printf("%.50f\n", x);return 0;
}

在这里插入图片描述

了解浮点数在内存存储后会明白浮点数最后存在内存中可能不准确.无法将所有准确数字存在内存中,导致最后取出来得到的结果会有精度位损失(精度位损失不一定是原来数据变小,也会发生增大)根据上面的例子以double类型为例:看出3.6存进去之后以保留后面50位小数形式打印它最后会得到原来的数字但是在后面十六位小数后会出现一些其他值,也就是发生精度损失,一般会显示6位小数,就感觉这个数最后还是3.6,
因为这是经过了一些相应的处理:最后使得十六小数后的那些数字为一些无法预测的数字
所以double类型数据一般我们主观认为前十六位数字是有效数字,因为后面数字可能会精度损失.
既然存在精度损失 ,浮点数和0.0比较时可以直接用==吗??
答案:不能!

2.浮点数变量如何和零值比较

浮点数在内存中的形式是二进制形式,而浮点数转换为二进制小数位数有些转换不了会经过相应转换,使得最后取出来的数字前十六数字是实际有效的,但并不排除有效数字后面的数字是0以外的不可预料的数字,这些数字仍然会参与运算,只是一般显示六位小数看不到这些数字的运算,但是浮点数之间用==比较则会出现问题!!

#include <stdio.h>
int main()
{double x = 1.0;double y = 0.1;if ((x - 0.9) == y){printf("相等\n");} else{printf("不相等\n");} return 0;
}

最后运行结果是什么?
正常思想1.0-0.9最后肯定会得到0.1输出结果会是相等
but!!
这是计算机在进行运算,计算机里的存放的这些数据可能会发生精度损失,所以结果不一定是相等

在这里插入图片描述

结果:不相等
那这他们具体是怎么样比较得出不相等的呢?

在这里插入图片描述

将它们各自的数据以保留50位小数形式打印出来
最后发现0.1和0.9数据存在计算机里最后会发生处理十六位有效数字后会有非0的数字
正常情况下这些数字我们看不到,只会看到0.100000,实际上在计算机里是存在的,
1.0在与0.9进行相减后以保留五十位小数位数显示出现的结果不会是0.1,
正常情况下我们看到的是0.100000是因为默认保留六位小数,后面的小数位会四舍五入最后得到的0.1,但是实际上更准确的是保留位数越多的,
所以最后和0.1实际上计算机做比较不会保留位数四舍五入进行比较而是所有位数都会进行比较.如果不相等,结果就是不相等,
而不相等的原因也正是因为有了那些存在计算机里进过处理后出现的精度损失的数字

因此,上面的比较方法是错误的,因为在计算机看来得到的数字并不是和我们想象的意义
那浮点数该如何比较呢?
应该进行范围精度比较!

两个浮点数作差得到的值 应该大于负精度同时小于正精度(这里精度用 EPS表示)
伪代码: (x-y)>-EPS&&((x-y)<EPS)
伪代码简洁形式:fabs(x-y)<EPS (fabs为数学库函数 表示得到常数的绝对值 使用需要包含头文件math.h)

精度是什么:就是和主观上保留十六位小数一样,产生的一个精度小数,表示任何数只要小于这个精度说明这个数在精度前面部分是0.000000后面部分可能是0可能是些非0 的数字
用精度区分了有效数字让计算机根据人的设计的精度来判断这个数除去小于精度的数字后得到的结果
精度可以是自己设定(通常是#define 宏定义),或者使用系统设定的精度
系统设定的精度有double类型数据的精度表示为DBL_EPSILON
float类型的精度表示为FLT_EPSILON
使用这两个精度需要包含头文件float.h
XXX_EPSILON是最小误差,是:XXX_EPSILON+n不等于n的最小的正数。
EPSILON这个单词翻译过来是’ε’的意思,数学上,就是极小的正数 ----来自百度 😃在这里插入图片描述
在这float.h的定义中找到这两个精度,可以看到是被宏定义的,它们表达意思也就是当0加上这个精度得到的结果不等于0,但是有很多的数加上0后的结果都不会等于0,但这个精度设定的意义表示的是最小的正数,即当比这个精度小的数字加上0后,最后的结果就会被当作0来看待!!!

#include<float.h> //使用下面两个精度,需要包含该头文件
//DBL_EPSILON //double 最小精度
//FLT_EPSILON //float 最小精度
//代码调整后
#include <stdio.h>
#include <math.h> //必须包含math.h,要不然无法使用fabs
#include <float.h> //必须包含,要不然无法使用系统精度
int main()
{double x = 1.0;double y = 0.1;printf("%.50f\n", x - 0.9);printf("%.50f\n", y);if (fabs((x - 0.9) - y) < DBL_EPSILON){ //原始数据是浮点数,我们就用DBL_EPSILONprintf("相等\n");} else{printf("不相等\n");} return 0;
}

上面代码引入了系统设定的精度,最后的结果是什么呢?

在这里插入图片描述

最后结果为相等,使计算机运算得到了我们想要的值
但是这不是随便设置的,有了这个精度值限制,最后x-0.9-0.1的绝对值在计算机经过运算后实际上是一个前面十六个有效数字都为0,而后面是一些无法确定的精度数字,而我们只需要,用得到的这个结果与设置的精度比较,如果是小于设置的精度,表示在这个精度前面的那些数字都是0,也就是告诉计算机,我们要两个浮点数比较是否相等的结果是通过两个浮点数相减,最后得到的数据是小于精度,满足这个条件则表示精度小数前面的数都是0后面的数忽略不计,
得到的结果就按零值看待,用这种方法就省略掉了最后损失的精度得到了想要的结果

除了用系统设置的精度外,我们可以自己用宏定义设置相应的精度

#include <stdio.h>
#include<math.h>
#define EPS 1E-16
int main()
{double x = 1.0;double y = 0.1;printf("%.50lf\n", x - 0.9);printf("%.50lf\n", y);printf("%.50lf\n", fabs((x - 0.9) - y));printf("%.50lf\n", EPS);if (fabs((x - 0.9) - y) < EPS) { printf("相等\n");} else{printf("不相等\n");} return 0;
}

上面代码为自己宏定义了精度EPS 为1E-16表示1的负十六次方

在这里插入图片描述

可以看到,当满足两个浮点数相减得到的数是小于我设置的这个精度EPS(1的负16次方)
表示前十六小数位都为0,后面的小数位数为精度位不用考虑,如果满足小于条件则得到的这个结果是比设置的精度还小的数字即这个数加上0实际上还是等于0,表示这个数与0等价,得到的结果就是这两个浮点数相等

知道了两个浮点数如何比较是否相等,那浮点数变量和零值如何比较呢?

下面这串代码结果是什么?

#include <stdio.h>
#include <math.h>
#include <float.h>
int main()
{
double x = 0.00000000000000000000001;
//if (fabs(x-0.0) < DBL_EPSILON){ //写法1
//if (fabs(x) < DBL_EPSILON){ //写法2
if(x > -DBL_EPSILON && x < DBL_EPSILON){ 
printf("x等于0\n");
} 
else
{
printf("x不等于0\n");
} return 0;
}

结果是x等于0, 尽管x小数位后面有一个1,但是这个1已经在设置的有效位数之后,被我们当做精度位来处理,而精度位就是可以直接省略的,
判断x是否等于0,我们只需要判断x是否大于负精度 同时小于正精度 也就是x的绝对值是否小于精度,满足小于 则x这个数加上0.0实际上是等同于0的,我们将x和零值等价
最后用这种方法判断出x是等于0的,后面的小数位1只是省略计算的精度位

三.指针变量与零值比较

指针变量用于存放指针,说的指针变量和零比较实际是用指针和零值的比较

1.常见的零值

0:数字常量0
‘\0’:字符常量’\0’它是以一个\加0组成的一个转义字符,通常用于作为字符串结束标志,实际上是一个字符,而字符是由ASCLL码转换而来’\0’转换为ASCLL码就是数字0,只是0的另一种形式
NULL:空指针 ,通常用于给指针变量初始化,防止野指针出现,而NULL实际上是(void*)0,
是将0强制类型转换为空类型的指针,也就是一个空类型的0地址,实质上也是一个数字0

2.指针变量如何和零值比较?

虽然零值有不同形式的存在,但是这样设计是为了适应不同类型的变量.
假设有一个 int *a =0;
看到这串代码是不是有一个奇怪的想法,a是一个指针变量存一个0干嘛,实际上存0表示的是0地址,也可以存在a里,但是由于我们人的惯性,0是一个常量,存在指针变量a里,看着也有点别扭
后面设置了一个类型NULL表示空指针 实际上也是0,以这种形式int *a=NULL;
表示这个指针变量里存着是一个空类型的0地址,就表达式空指针的意思
这种形式更让我们所接受.

指针应该如何和零值比较?
下面这三种类型的代码你更倾向哪种?

int *a=NULL;
> if(a)if(!a)  (1)
> if(a= =0)if(a!=0)  (2)
> if(a==NULL)if(a!=NULL) ( 3)

第一种:让我们一眼看起来和bool类型与零值比较一样,不推荐指针变量这样使用
第二种:指针变量本身存放的是0,但是是以NULL形式存在,此时又和0比较让人误解,不推荐
第三种:指针变量里为NULL和NULL比较 符合对应的设计,推荐!!!
结论:指针变量和零值比较,这个零值要用对应的NULL表示更让人一目了然.

在这里插入图片描述

四.总结

本篇博客介绍了三种类型变量和零值的比较
,bool变量和零比较: bool用来表示真假, 在c99中采用,在c90中直接使用当前变量的数作为bool, 不需要再和其他数比较再得出真假,直接用本身作为真假的判断即可

浮点型变量和零比较:因为浮点型变量可能存在精度损失,不能直接和零值进行比较,应采用作差法用范围精度比较,用自己设置的精度或者系统精度,当这个数的绝对值小于设置的这个精度时,表示这个数等于零值.
指针变量和零比较:指针变量和零值比较,零值尽可能用NULL形式能让人一目了然.

在这里插入图片描述

写文不易,给个一键三连,支持下吧~~~

这篇关于猿创征文丨学C/C++进来看看--你可能都不清楚的三种变量和零值比较的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/358451

相关文章

百度/小米/滴滴/京东,中台架构比较

小米中台建设实践 01 小米的三大中台建设:业务+数据+技术 业务中台--从业务说起 在中台建设中,需要规范化的服务接口、一致整合化的数据、容器化的技术组件以及弹性的基础设施。并结合业务情况,判定是否真的需要中台。 小米参考了业界优秀的案例包括移动中台、数据中台、业务中台、技术中台等,再结合其业务发展历程及业务现状,整理了中台架构的核心方法论,一是企业如何共享服务,二是如何为业务提供便利。

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

变量与命名

引言         在前两个课时中,我们已经了解了 Python 程序的基本结构,学习了如何正确地使用缩进来组织代码,并且知道了注释的重要性。现在我们将进一步深入到 Python 编程的核心——变量与命名。变量是我们存储数据的主要方式,而合理的命名则有助于提高代码的可读性和可维护性。 变量的概念与使用         在 Python 中,变量是一种用来存储数据值的标识符。创建变量很简单,

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现