表达式求值:隐藏的大佬

目录

一.学习前的思考

1.隐式类型转换

2.显示类型转换

3.操作符的属性

二.隐式类型转换

1.整形提升

  2.算数转换

三.显示类型转换

四.表达式的执行顺序


一.学习前的思考

我们在写代码的过程中,已经不知不觉用到了许多表达式,

比如,乘法表的打印中

#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下结果:
Linus下结果:
对于不同编译器,有不同的结果,执行顺序不一样,我们叫他问题表达式。
我们可以加上(),()的优先级是最高的。
我们可以把这个代码优化为:
int main() {
	int i = 1;
	int ret = ((++i) + (++i)) + (++i);  //加上(),执行顺序唯一
	printf("%d\n", ret);
	printf("%d\n", i);
	return 0;
}
总结 :我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。

代码我放在:登录 - Gitee.com

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值