目录
一.学习前的思考
我们在写代码的过程中,已经不知不觉用到了许多表达式,
比如,乘法表的打印中
#include <stdio.h>
int main() {
int i = 0;
int j = 0;
for (i = 1; i <=9; i++) //这就是一个表达式 {
for (j = 1; j <= i; j++) {
printf("%d * %d = %d ", i, j, i * j);
}
printf("\n");
}
return 0;
}
在比如:
#include <stdio.h>
int main() {
int a = 0;
int b = 0;
int c = a + b; //表达式求值
return 0;
}
这样看起来,表达式求值好像稀松平常,总是在用。但其实, 表达式求值是个隐藏大佬,其中包含很多知识。
1.隐式类型转换
2.显示类型转换
3.操作符的属性
二.隐式类型转换
1.整形提升
C的整型算术运算总是至少以缺省(缺省就是默认)整型类型的精度来进行的。
为了保持这个精度,也为了通用性,就要把低于4字节的符号进行整形提升。
例子:
int main() {
char a = 5;
char b = 126;
char c = a + b;
printf("%d \n", c);
}
大家觉得结果会是什么:
一定出乎意料:
这是因为对char类型进行了 整形提升。整形提升会发生截断。
整形提升是按照变量的数据类型的符号位来提升的(整形提升用到了源码,反码,补码,后续我会写一篇文章讲解)
符号位是1 ,用1补齐32位
符号位是0,用0补齐32位
#include <stdio.h>
int main() {
char a = 5; //5是int型,32bit位
//00000000000000000000000000000011
//a只接收 8bit位 00000011
//整形提升 : 00000000000000000000000000000011
char b = 126;
//00000000 00000000 00000000 1111110
//b只接收 8bit位 01111110
//整形提升 : 00000000 00000000 00000000 01111110
char c = a + b;
//000000000000000000000000 00000011 a
//000000000000000000000000 01111110 b
//111111111111111111111111 10000011 -c
//c 只接收 8bit位 : 10000011
//整形提升 :
//11111111 11111111 11111111 10000011 -补码
//11111111 11111111 11111111 10000010 -反码 = 补码 - 1
//10000000 00000000 00000000 01111101 -原吗
// -125
printf("%d \n", c);
return 0;
}
例子2:
#include <stdio.h>
int main() {
//用16进制是因为,十进制short就完美接收了
char a = 0x2003; //发生截断
short b = 0xb1100; //发生截断
int c = 0xb2000000;
if (a == 0x2003)
printf("a");
if (b == 0xb1100)
printf("b");
if (c == 0xb2000000)
printf("c");
return 0;
}
结果跟我们预想的一样:
例子3:
int main() {
char c = 1;
printf("%u\n", sizeof(c)); //1
printf("%u\n", sizeof(+c)); //4 发生整形转换
printf("%u\n", sizeof(-c));//4 发生整形转换
return 0;
}
2.算数转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类
型,否则操作就无法进行。下面的层次体系称为
寻常算术转换
。
例子:
//寻常算术转换
int main() {
double a = 7.5; //从内存大的转为内存小的
int b = a; //会出现精度丢失
printf("%d\n", b);
return 0;
}
结果:
三.显示类型转换
显示类型转换也叫强制转换,如;
int main() {
char a = (char)2003;
printf("%d\n", a);
return 0;
}
结果:
可见强制转换并不安全,在以后的编程生涯里最好不要常用(我血泪的经历,我写的代码出问题强制转换从来都没有对代码产生正向结果)
四.表达式的执行顺序
复杂表达式的求值有三个影响的因素:
1.
操作符的优先级
2.
操作符的结合性
3.
是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
例子:
int main() {
int a = 1;
int b = 2;
int c = a + a * b + b;
//对于这个表示式:对a + a * b,先执行 a * b ,再a+
//对a* b + b;先执行 a * b ,再+b;
//因为操作符的属性只能确定相邻的操作符的执行顺序
printf("%d\n", c);
return 0;
}
那可能有的人就有疑问了,对于这个表达式,不相邻的无论执行谁,结果都一样,
那我们就来看一个不一样的
int main() {
int c = 0;
c + --c;
printf("%d\n", c);
return 0;
}
对于这个表达式,有两种走向:
例子2:
int main() {
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}
vs下结果:
![](https://i-blog.csdnimg.cn/blog_migrate/febf333698aee98771570a4086188c97.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f7626be59fda9ba54d83c0a7513add88.png)
Linus下结果:
![](https://i-blog.csdnimg.cn/blog_migrate/0ef478862662050dc52f2366b180dc24.png)
对于不同编译器,有不同的结果,执行顺序不一样,我们叫他问题表达式。
我们可以加上(),()的优先级是最高的。
我们可以把这个代码优化为:
int main() {
int i = 1;
int ret = ((++i) + (++i)) + (++i); //加上(),执行顺序唯一
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}
总结
:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。
代码我放在:登录 - Gitee.com