《C语言深度剖析》第一章 关键字详解 p2 C语言从入门到入土(进阶篇)

目录

1.signed、unsigned

1.1整形在内存的存储

1.2signed(有符号数)

1.3unsigned(无符号数)

2 if else 组合

3 各种变量与“零值”进行比较

3.1 bool 变量与"零值"进行比较

3.2 float 变量与"零值"进行比较

3.3 指针变量与“零值”进行比较


本章节文章是作者通过观看《C语言深度剖析》等各种资料总结的精华,基础部分省略了不少,是为了让大家能够更加深入了解C语言的魅力!因为为了避免与之前的文章发生赘述,所以就直接讲作者认为的精华部分哈!现在正文开始! 

 谁都不能阻挡你成为更优秀的人。

1.signedunsigned

1.1整形在内存的存储

 也就是说,在补码进去之前是不管b的,是进入空间之后再转化为b(同时看b的类型算出值)(此时才看b的类型)。

总结:数据在存储时,是先转换为二进制,同时开辟了一块空间,然后将数据源储存进空间的时候再看其类型,然后解释其二进制位。(这里可能会发生整型提升)

PS:补充一个二进制快速转换口诀,n次方就后面n个0,前面一个1。

 

1.2signed(有符号数)

首先,对于有符号数,一定要能表示该数据是正数还是负数。所以我们一般用最高比特位来进行充当符号位。
原码、反码、补码
计算机中的有符号数有三种表示方法,即原码、反码和补码。
三种表示方法均有符号位和数值位两部分,符号位都是用 0 表示 ,用 1 表示 ,而数值位三种表示方法各不相同。
如果一个数据是负数,那么就要遵守下面规则进行转化:
原码:直接将二进制按照正负数的形式翻译成二进制就可以。
反码:将原码的符号位不变,其他位依次按位取反就可以得到了。
补码:反码 + 1 就得到补码。
如果一个数据是正数,那么它的原反补都相同。

1.3unsigned(无符号数)

不需要转化,也不需要符号位,原反补相同。
对于整形来说:数据存放内存中其实存放的是补码。
为什么都是补码
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理; 同
时,加法和减法也可以统一处理( CPU 只有加法器)。此外,补码与原码相互转换,其运算过程是相同的,不
需要额外的硬件电路。
取值范围
总结规律:整数的取值范围
无符号: [ 0 , 2 ^n - 1 ]
有符号: [ - 2 ^ ( n - 1 ), 2 ^ ( n - 1 ) - 1 ]

2 if else 组合

语法结构:
//1
if ( 表达式 )
        语句 ;
//2
if ( 表达式 )
        语句1 ;
else
        语句2 ;
//3. 多分支
if ( 表达式 1 )
        语句1 ;
else if ( 表达式 2 )
        语句2 ;
else
        语句3 ;
//4. 嵌套
if ( 表达式 1 ){
        语句1 ;
        if ( 表示式 x ){
                语句x ;
        }
        else {
                语句y ;
        }
}
else if ( 表达式 2 ){
                语句2 ;
}
else {
        语句3 ;
}

3 各种变量与“零值”进行比较

3.1 bool 变量与"零值"进行比较

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

 PS:理论上,表示真假,需要一个bit就够了,不过这个问题,还是要取决于编译器的理解。vs2013中认为是1个字节。

但是:

//在vs中,看看下面的代码 
//测试代码2 
#include <stdio.h>
#include <windows.h> 
int main() 
{ 
//在vs中,光标选中BOOL,单击右键,可以看到转到定义,就能看到BOOL是什么 
BOOL ret = FALSE; 
ret = TRUE; 
printf("%d\n", sizeof(ret)); 
//输出结果是4,因为在源代码中,是这么定义的:typedef int BOOL; 
system("pause"); 
return 0; 
}

这都是Microsoft自己搞的一套BOOL值。在vs中转到BOOL对应的头文件,翻到最上面,就能看到微软的版权信息。好了,该听谁的??现在有两种1/4。        

微软?强烈不推荐,因为好的习惯是:一定要保证代码的跨平台性,微软定义的专属类型,其他平台不支持。(以后在语言编程层面上,凡是直接使用和平台强相关的内容,我们都不推荐。(不是针对谁哈))
跨平台性?
我们可以看到上面测试代码 1 ,和测试代码 2 vs2013 下都能编过(微软系的) ,
但是在 Linux ( centos 7 ), 测试代码 1 ,是可以编过的 ( 因为是标准啊 ) ,但是测试代码 2就过不了。
所以,后面万一要用 bool ,强烈推荐 C99 标准的,摒弃微软
总结:
1. 优先使用 c90 , 就是我们之前以及后面一直用的方式
2. 万一非得使用 bool ,推荐 c99 标准,不推荐 MS 自定义。
那么, 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 类型,直接判定,不用操作符进行和特定值比较。

3.2 float 变量与"零值"进行比较

浮点数在内存中存储,并不想我们想的,是完整存储的,在十进制转化成为二进制,是有可能有精度损失的。
注意这里的损失,不是一味的减少了,还有可能增多。浮点数本身存储的时候,在计算不尽的时候,会 四舍五入 ”或者其他策略

结论:因为精度损失问题,两个浮点数,绝对不能使用==进行相等比较.

那么两个浮点数该如何比较呢?
应该进行范围精度比较:
#include<float.h> 
//使用下面两个精度,需要包含该头文件 
DBL_EPSILON //double 最小精度 
FLT_EPSILON //float 最小精度
//伪代码 
if((x-y) > -精度 && (x-y) < 精度){ 
//TODO 
}
//伪代码-简洁版 
if(fabs(x-y) < 精度){ 
//fabs是浮点数求绝对值
//TODO 
}
精度:
自己设置?后面如果有需要,可以试试,通常是宏定义。
使用系统精度?暂时推荐
//代码调整后 
#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"); 
}
system("pause"); 
return 0; 
}
两个精度定义
#define DBL_EPSILON 2.2204460492503131e-016 /* smallest such that 1.0+DBL_EPSILON != 1.0 */
#define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON !=
1.0 */
XXX_EPSILON 是最小误差 , 是: XXX_EPSILON + n 不等于 n 的最小的正数。
EPSILON 这个单词翻译过来是 'ε' 的意思,数学上,就是极小的正数

 所以我们再回来讲float和0的比较:

#include <stdio.h> 
#include <math.h> 
#include <float.h> 
#include <windows.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("you can see me!\n"); 
}
else{
printf("oops\n"); 
}
system("pause"); 
return 0; 
}

 有朋友就有疑问了?

//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 的概念。

3.3 指针变量与零值进行比较

其实NULL 和 '/0' 和 0 都是0。只不过因为编译器编译的时候需要相同的类型进行计算,所以就有了“各种不同的0”。

else 到底与哪个 if 配对呢?

其实并不是看着这样的理解,推荐的写法是:

 总结:else 匹配采取就近原则

今天的内容就到这里了哈!!!

要是认为作者有一点帮助你的话!

就来一个点赞加关注吧!!!当然订阅是更是求之不得!

最后的最后谢谢大家的观看!!!

你们的支持是作者写作的最大动力!!!

下期见哈!!!

猜你喜欢

转载自blog.csdn.net/weixin_62700590/article/details/121780619