《C语言深度剖析》第二章 符号详解 p2(完结) C语言从入门到入土(进阶篇)

目录

1. ++、--操作

1.1前置++(--),后置++(--)

1.2深刻理解 a++  

 1.3 补充

1.4 表达式匹配,贪心算法

2.1 2/(-2) 的值是多少,深度理解取余/取模运算

2.1.1 关于“取整”你得知道

2.1.2 向0取整

2.1.3 向-∞取整

2.1.4 向+∞取整

 2.1.5 四舍五入

 2.2 聊聊取模

2.2.2 是什么决定了这种现象

2.2.3 取余和取模一样吗?

 2.2.4 如果参与运算的数据,不同符号呢?

2.2.5 总结


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

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

1. ++--操作

1.1前置++(--),后置++(--)

前置 ++ , 先自增在使用
后置 ++ , 先使用在自增

1.2深刻理解 a++  

#include <stdio.h>
#include <windows.h>
int main()
{
int a = 0xDD;
int b = a++; //有b接收,那么a的先使用是将a的值(内容),放到b中
int c = 0xEE;
c++; //没有接收方,那么"先使用",如何理解?
system("pause");
return 0;
}

在没有人用的时候(没有接收方),前置和后置是一样的。 

下面,我们直接在汇编角度,理解 a ++ vs2013 下更直观,我们在 vs2013 下直接查看(
Linux同理) 截取核心部分汇编代码
结论:a++完整的含义是先使用,在自增。如果没有变量接收,那么直接自增(或者所谓使用,就是读取进寄存器,然后没有然后=。=)。

 1.3 补充

int j = ( ++ i ) + ( ++ i ) + ( ++ i );   //没错又是他,在不同编译器的结果不同
本质:是因为上面表达式的"计算路径不唯一"(为什么?编译器识别表达式,是同时加载至寄存器,还是分批加载,完全不确定)导致的。

1.4 表达式匹配,贪心算法

printf("%d\n", a++++ + b); //自动匹配失败
printf("%d\n", a++ + ++b); //自行分离匹配,非常不推荐,不过能看出空格的好处

 就是你连着++--的时候计算机会自行匹配,但不一定会匹配正确

 原则就是会尽量往左边匹配更多的符号(贪心)。

2.1 2/(-2) 的值是多少,深度理解取余/取模运算

2.1.1 关于取整你得知道

2.1.2 0取整

#include <stdio.h>
#include <windows.h>
int main()
{
        //本质是向0取整
        int i = -2.9;
        int j = 2.9;
        printf("%d\n", i); //结果是:-2
        printf("%d\n", j); //结果是:2
        system("pause");
        return 0;
}

有一个trunc取整函数,同作用

2.1.3 -∞取整

#include <stdio.h>
#include <math.h> //因为使用了floor函数,需要添加该头文件
#include <windows.h>
int main()
{
//本质是向-∞取整,注意输出格式要不然看不到结果
printf("%.1f\n", floor(-2.9)); //-3
printf("%.1f\n", floor(-2.1)); //-3
printf("%.1f\n", floor(2.9)); //2
printf("%.1f\n", floor(2.1)); //2
system("pause");
return 0;
}

2.1.4 +∞取整

#include <stdio.h>
#include <math.h>
#include <windows.h>
int main()
{
//本质是向+∞取整,注意输出格式要不然看不到结果
printf("%.1f\n", ceil(-2.9)); //-2
printf("%.1f\n", ceil(-2.1)); //-2
printf("%.1f\n", ceil(2.9)); //3
printf("%.1f\n", ceil(2.1)); //3
system("pause");
return 0;
}
 

 

 2.1.5 四舍五入

#include <stdio.h>
#include <math.h>
#include <windows.h>
int main()
{
//本质是四舍五入
printf("%.1f\n", round(2.1));
printf("%.1f\n", round(2.9));
printf("%.1f\n", round(-2.1));
printf("%.1f\n", round(-2.9));
system("pause");
return 0;
}

结论:浮点数(整数/整数),是有很多的取整方式的。

PS:C默认向0取整。 

 2.2 聊聊取模

取模概念:

如果 a d 是两个自然数, d 非零,可以证明存在两个唯一的整数 q r ,满足 a = q * d + r 0 ≤ r < d 。其中, q被称为商,r 被称为余数。

我们平时都是用的正数的取模,所以并没有发现什么问题,但是想一想,我们如果遇到负数的取模呢? 

1

#include <stdio.h>
#include <windows.h>
int main ()
{
int a = 10 ;
int d = 3 ;
printf ( "%d\n" , a % d ); // 结果是 1
// 因为: a=10,d=3,q=3,r=1 0<=r<d(3)
// 所以: a = q*d+r -> 10=3*3+1
system ( "pause" );
return 0 ;
}
2
#include <stdio.h>
#include <windows.h>
int main ()
{
int a = - 10 ;
int d = 3 ;
//printf("%d\n", a/d); //C 语言中是 -3 ,很好理解
printf ( "%d\n" , a % d );
system ( "pause" );
return 0 ;
} 

 但是2在python中我们发现是2?
 结论:很显然,上面关于取模的定义,并不能满足语言上的取模运算

因为在 C 中,现在 - 10 % 3 出现了负数,根据定义:满足 a = q * d + r 0 ≤ r < d C 语言中的余数,是不满足定义的, 因为,r < 0 了。
故,大家对取模有了一个修订版的定义:
如果 a d 是两个自然数, d 非零,可以证明存在两个唯一的整数 q r ,满足 a = q * d + r , q 为整数,且 0 ≤ | r | < | d | 。其中, q 被称为商, r 被称为余数。
有了这个新的定义,那么 C 中或者 Python 中的 取模 ,就都能解释了。
解释 C : - 10 = ( - 3 ) * 3 + ( - 1 )
解释 Python - 10 = ? * 3 + 2 , 其中,可以推到出来 , '?' 必须是-4 ( 后面验证 ). - 10 =
- 4 * 3 + 2 ,才能满足定义。
所以,在不同语言,同一个计算表达式,负数 取模 结果是不同的。我们可以称之为分别叫做正余数 和 负余数

2.2.2 是什么决定了这种现象

由上面的例子可以看出,具体余数 r 的大小,本质是取决于商 q 的。
而商,又取决谁呢?取决于除法计算的时候,取整规则。
简单说,要先算商,再算余数,而商的取法前面说了4种,这就要看编译器。

2.2.3 取余和取模一样吗?

这两个并不能严格等价 ( 虽然大部分情况差不多 ) 取余或者取模,都应该要算出商,然后才能得出余数。
本质 1 取整
取余:尽可能让商,进行向0取整。
取模:尽可能让商,向-∞方向取整。
故:
C中%,本质其实是取余。
Python % ,本质其实是取模。
理解链:
对任何一个大于 0 的数, 对其进行0向取整和-∞取整 取整方向一致的。故取模等价于取余
对任何一个小于 0 的数, 对其进行0向取整和-∞取整 取整方向相反的。故取模不等价于取余

同符号数据相除,得到的商,一定是正数(正数 vs 正整数),即大于 0
故,在对其商进行取整的时候,取模等价于取余。
本质 2 符号
参与取余的两个数据,如果同符号,取模等价于取余

 2.2.4 如果参与运算的数据,不同符号呢?

#include <stdio.h>
#include <windows.h>
int main()
{
printf("%d\n", -10 / 3); //结果:-3
printf("%d\n\n", -10 % 3); //结果:-1 为什么? -10=(-3)*3+(-1)
printf("%d\n", 10 / -3); //结果:-3
printf("%d\n\n", 10 % -3); //结果:1 为什么?10=(-3)*(-3)+1
system("pause");
return 0;
}
明显结论:如果不同符号,余数的求法,参考之前定义。而余数符号,与被除数相同()。
但是:

好像不是这样的,为什么呢?

重新看看定义:
如果 a d 是两个自然数, d 非零,可以证明存在两个唯一的整数 q r ,满足 a = q * d + r , q 为整数,且 0 ≤ | r | < | d | 。其中, q 被称为商, r 被称为余数。
a = q * d + r 变换成 r = a - q * d 变换成 r = a + ( - q * d )
对于: x = y + z ,这样的表达式, x 的符号 与 | y | | z | 中大的数据一致。
r = a + ( - q * d ) 中, | a | |- q * d | 的绝对值谁大,取决于商 q 的取整方式。
c 是向 0 取整的,也就是 q 本身的绝对值是减小的。
如:
- 10 / 3 =- 3.333 . 33 0 取整 - 3. a =- 10 | 10 | , - q * d =- ( - 3 ) * 3 = 9 | 9 |
10 /- 3 =- 3.333 . 33 0 取整 - 3. a = 10 | 10 | , - q * d =- ( - 3 ) * ( - 3 ) =- 9 | 9 |
绝对值都变小了
python 是向 - 取整的,也就是 q 本身的绝对值是增大的。
- 10 / 3 =- 3.333 . 33 '//' - 取整 - 4. a =- 10 | 10 | , - q * d =- ( - 4 ) * 3 = 12 | 12 |
10 /- 3 =-- 3.333 . 33 '//' - 取整 - 4. a = 10 | 10 | , - q * d =- ( - 4 ) * ( - 3 ) =- 12 | 12 |
绝对值都变大了
结论:如果参与取余的两个数据符号不同,在 C 语言中 ( 或者其他采用向 0 取整的语言如: C ++ Java ) ,余数符号,与被除数相同。

2.2.5 总结

  1. 浮点数(或者整数相除),是有很多的取整方式的。
  2. 如果ad是两个自然数,d非零,可以证明存在两个唯一的整数 q r,满足 a = q*d + r , q 为整数,且0 ≤ |r| < |d|。其中,q 被称为商,r 被称为余数。
  3. 在不同语言,同一个计算表达式,取模结果是不同的。我们可以称之为分别叫做正余数 和 负余数。
  4. 具体余数r的大小,本质是取决于q的。而商,又取决于除法计算的时候,取整规则。
  5. 取余vs取模: 取余尽可能让商,进行向0取整。取模尽可能让商,向-∞方向取整。
  6. 参与取余的两个数据,如果同符号,取模等价于取余。
  7. 如果参与取余的两个数据符号不同,在C语言中(或者其他采用向0取整的语言如:C++Java),余数符号,与被除数相同。(因为采用的向0取整)

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

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

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

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

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

下期见哈!!!

猜你喜欢

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