引言
0不管是在生活中还是在学习中都是十分常见的,这里我们先看一看关于0的历史沿革,然后再讨论C语言与0的”故事“
0是极为重要的数字,关于0这个数字概念在其它地区很早就有。公元前3000年,巴比伦人就已经懂得使用零来避免混淆。古埃及早在公元前2千年就有人在记帐时用特别符号来记载零。玛雅文明最早发明特别字体的0。玛雅数字中0以贝壳模样的象形符号代表。
标准的0这个数字由古印度人在约公元5世纪时发明。他们最早用黑点表示零,后来逐渐变成了“0”。在东方国家由于数学是以运算为主(西方当时以几何并在开头写了“印度人的9个数字,加上阿拉伯人发明的0符号便可以写出所有数字)。由于一些原因,在初引入0这个符号到西方时,曾经引起西方人的困惑, 因当时西方认为所有数都是正数,而且0这个数字会使很多算式、逻辑不能成立(如除以0),甚至认为是魔鬼数字,而被禁用。直至约公元15,16世纪0和负数才逐渐给西方人所认同,才使西方数学有快速发展。
0的另一个历史:0的发现始于印度。公元前2000年左右,古印度婆罗门教最古老的文献《吠陀》已有“0”这个符号的应用,当时的0在印度婆罗门教表示无(空)的位置。约在6世纪初,印度开始使用命位记数法。7世纪初印度大数学家葛拉夫.玛格蒲达首先说明了0的0是0,任何数加上0或减去0得任何数。遗憾的是,他并没有提到以命位记数法来进行计算的实例。也有的学者认为,0的概念之所以在印度产生并得以发展,是因为印度佛教中存在着“绝对无”这一哲学思想。公元733年,印度一位天文学家在访问现伊拉克首都巴格达期间,将印度的这种记数法介绍给了阿拉伯人,因为这种方法简便易行,不久就取代了在此之前的阿拉伯数字。这套记数法后来又传入西欧。
了解了0之后,我们进入正题:C语言与0的不解之缘
bool 变量与"零值"进行比较
bool类型—C99定义
首先,C语言有没有bool类型?
c99之前,主要是c90是没有的,目前大部分书,都是认为没有的。因为书,一般都要落后于行业。但是c99引入了_Bool类型(你没有看错,_Bool就是一个类型,不过在新增头文件stdbool.h中,被重新用宏写成了bool,为了保证C/C++兼容性)
bool类型源码如下:
#define bool _Bool //C99中是一个关键字,后续可以使用bool
#define false 0 //假
#define true 1 //真
由此可见bool类型实际就是来表示真假的
PS:理论上,表示真假,需要一个bit就够了,不过这个问题,还是要取决于编译器的理解。vs中认为是1个字节(其它编译器上就不一定是一个字节了)。那么问题来了,后面代码该怎么写?因为目前编译器对C99特性支持的并不全面,我们后面依旧默认使用C90的认识去编码即可,使用int表示真、假。具体要结合实际况情去定
BOOL类型—Microsoft专利
在vs中,光标选中BOOL,双击,可以看到转到定义,就能看到BOOL是什么
输出结果是4,因为在源代码中,是这么定义的:typedef int BOOL;
这也就可以解释为什么结果是4了
这是什么情况?编译器竟然也能通过。
这都是Microsoft自己搞的一套BOOL值,我们这里不推荐使用,因为在其他编译器上可能会不通过,而bool是C99标准的,跨平台性比BOOL高,所以,后面万一要用bool,我们最好使用C99标准的。
C中如何进行 bool 值与0比较呢?
#include <stdio.h> #include <stdbool.h> #include <windows.h> int main() { int pass = 0; //0表示假,C90,我们习惯用int表示bool //bool pass = false; //C99 if (pass == 0){ //理论上可行,但此时的pass是应该被当做bool看待的,==用来进行整数比较,不推荐 //TODO } if(pass == false){ //不推荐,尽管在C99中也可行 //TODO } if(pass){//推荐 //TODO } //理论上可行,但此时的pass是应该被当做bool看待的,==用来进行整数比较,不推荐 //另外,非0为真,但是非0有多个,这里也不一定是完全正确的 if (pass != 1){ //TODO } if(pass != true){ //不推荐,尽管在C99中也可行 //TODO } if(!pass){ //推荐 //TODO } system("pause"); return 0; }
结论:bool类型,直接判定,不用操作符进行和特定值比较。
float 变量与"零值"进行比较
这里比较复杂,要理清楚更细节的内容,需要知道浮点数在内存中的存储原理,大家如果不清楚的话可以移步到我的另外一篇博客“浮点型存储”。
浮点数在内存中存储,并不想我们想的,是完整存储的,在十进制转化成为二进制,是有可能有精度损失的。注意:这里的损失,不是一味的减少了,还有可能增多。浮点数本身存储的时候,在计算不尽的时候,会“四舍五入”或者其他策略,请看下面两个代码:
结论:因为精度损失问题,两个浮点数,绝对不能使用==进行相等比较
那么两个浮点数该如何比较呢?应该进行范围精度比较
范围精度比较
伪代码如下:
if((x-y) > -精度 && (x-y) < 精度) { //TODO } //伪代码-简洁版 if(fabs(x-y) < 精度) { //fabs是浮点数求绝对值 //TODO }
精度:
自己设置,后面如果有需要,可以试试,通常是宏定义。
暂时推荐使用系统精度
#include //使用下面两个精度,需要包含该头文件
DBL_EPSILON //double 最小精度
FLT_EPSILON //float 最小精度
#include //使用下面两个精度,需要包含该头文件
DBL_EPSILON //double 最小精度
FLT_EPSILON //float 最小精度
//代码调整后 #include <stdio.h> #include <math.h> //必须包含math.h,要不然无法使用fabs #include <float.h> //必须包含,要不然无法使用系统精度 #include <windows.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_EPSILON printf("you can see me!\n"); } else{ printf("oops\n"); } return 0; }
两个精度定义
double类型的精度定义
#define DBL_EPSILON | 2.2204460492503131e-016 |
float类型的精度定义
#define FLT_EPSILON |
1.192092896e-07F |
XXX_EPSILON是最小误差,是: XXX_EPSILON+n不等于n的最小的正数。
EPSILON这个单词翻译过来是'ε'的意思,数学上,就是极小的正数
拓展:
x > -DBL_EPSILON && x < DBL_EPSILON: 为何不是>= && <= 呢?
XXX_EPSILON是最小误差,是:XXX_EPSILON+n不等于n的最小的正数。
XXX_EPSILON+n不等于n的最小的正数: 有很多数字+n都可以不等于n,但是XXX_EPSILON是最小的,but,
XXX_EPSILON依旧是引起不等的一员。
换句话说:fabs(x) <= DBL_EPSILON(确认x是否是0的逻辑),如果=,就说明x本身,已经能够引起其他和他+-的数据本身的变化了,这个不符合0的概念,所以不应该加“=”
指针变量与“零值”进行比较
NULL, '\0', 0的整体理解
上面三个打印结果是0,0,0,也就是说这NULL在数值上是等于0的;但是呢,他们本身的类型是不同的,在什么时候能体现出来呢?
比如说:char *p = 0;
这行代码本身是有点奇怪的,它在这里把整数赋给了指针,编译器在编译的过程中可能会出现告警,当然了,在不同的编译器中可能会出现不一样的结果,比如vs就没有出现告警
所以我们在C语言当中就使用NULL来组织,这里又会有一些疑问,这个NULL是怎么变成0的呢?其实是强制类型转化,转到定义可以看到:
我们可以看到这里的NULL其实就是一个用宏定义的,它就是一个0值,只不过把0的类型变成了void*
强制类型转化
把字符串“123456”转化为int型“123456”,由七个字节转化成四个字节,这个应该叫做真实的转化
真实的转化是需要我们编写算法,使用相关库函数进行的转化
而强制类型转化其实是用不同的类型去理解这个数据,这个数据本身没有改变
就好比如我们刚开始对一个人的印象是不好的,但是后来因为他做了一些好事,让你改变了对他的看法,但是他本身却没有发生改变,这其实就是强制类型转化
例:把1111 1111 1111 1111 1111 1111 1111 1111 这个二进制码由signed int 输出改为由unsigned int 形式输出,但是这个数据本身没有发生变化,这就是强制类型转化
所以,所谓的强制类型转化并不改变该数据在内存当中的任何二进制组合,只改变我们如何去解释该二进制;真实的类型转化改变内存中的数据。
指针变量与“零值”进行比较的 if 语句
int * p = NULL; //定义指针一定要同时初始化
(A)写法: p 是整型变量?容易引起误会,不好。尽管 NULL 的值和 0 一样,但意义不同。
(B)写法: p 是 bool 型变量?容易引起误会,不好。
(C)写法:这个写法才是正确的,但样子比较古怪。为什么要这么写呢?是怕漏写一个“=”号,正常写法漏掉一个“=”:if(p = NULL),这个表达式编译器当然会认为是正确的,但却不是要表达的意思。如果用(C)的写法,漏掉“=”时:if(NULL=p),此时编译器会立刻报错,相当于有了一个自检查。
所以我们推荐第三种写法
写在最后:
以上就是C语言和0的”故事“了,如果有错误或者不严谨的地方,希望读者能够不吝指教,当然,如果你觉得写的还不错,不要忘了点赞、收藏、关注哦!