表达式求值的相关知识补充

前言

  1. 表达式求值的顺序是由操作符的优先级结合性决定。
  2. 而我们在进行表达式求值时,有些表达式的存放操作数的变量类型不同,为了能够进行运算,在求值的过程中可能需要转换为相同类型

1. 隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符(char)短整型操作数(short)在使用之前被转换为普通整型,这种转换称为整型提升。

1.1 整型提升

整形提升的意义

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。
所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

整形提升的实现过程:

char类型(1 Byte)与short类型(2 Byte)的变量在内存中不到4个字节,在运算过程中会将其先整型提升为 int类型(4Byte)。
具体方式如下:

  1. 将数据扩充到 32个bit 位
  2. 因为char类型 与 short 类型皆为有符号类型数,所以在定义时最高位默认为符号位,在整形提升时,扩充位上用符号位上的数填充。

示例:

  1. 负数的整形提升
    char c1 = -1;
    变量c1的二进制位(补码)中只有8个比特位: 1111111
    因为 char 为有符号的char, 所以整形提升的时候,高位补充符号位,即为1 提升之后的结果是: 11111111111111111111111111111111
  2. 正数的整形提升
    char c2 = 1;
    变量c2的二进制位(补码)中只有8个比特位: 00000001 因为 char 为有符号的
    char 所以整形提升的时候,高位补充符号位,即为0 提升之后的结果是: 00000000000000000000000000000001
  3. 无符号整形提升,高位补0
//示例1:
int main()
{
	char a = 0Xb6;
	//在内存中的存储形式:10110110(补码)
	//首位默认为符号位
	short b = 0Xb600;
	//1011011000000000(补码)
	//首位默认为
	int c = 0Xb6000000;
	//10110110000000000000000000000000(补码)
	//以0X位前缀,表明数据为16进制
	//每4个二进位表示1位16进制数位
	//b为11
	if(a == 0Xb6)
	//变量a 整型提升为:
	//11111111111111111111111110110110(补码)
	//11111111111111111111111110110101
	//10000000000000000000000001001010(-74)
	{
		printf("a\n");
	}
	if(b == 0Xb600)
	//11111111111111111011011000000000(补码)
	//11111111111111111011010111111111
	//10000000000000000100101000000000(-18944)
	{
		printf("b\n");
	}
	if(c == 0Xb6000000)
	{
		printf("c\n");
	}
	//只能打印出字符 'c'
	
	return 0;
}

//示例2:
int main()
{
	char c = 1;
	printf("%d\n",sizeof(c));
	//1个字节
	printf("%d\n",sizeof(+c));
	//4个字节
	printf("%d\n",sizeof(-c));
	//4个字节
	//只要参与运算就会整型提升
	
	return 0;
}

1.2 算数转换

如果某个操作符的各个操作数属于不同的类型
那么,除非其中一个操作数的转换为另一个操作数的类型
否则,操作就无法进行。
下面的层次体系称为寻常算术转换:
在这里插入图片描述

float f = 3.14;
int i = f;
//浮点数赋值,或强制转换为整形,或精度丢失

2. 表达式运算的规则

在复杂表达式的求值过程中,计算有一套自己的运算规则(区别于不同的编译器,运算规则的细节上可能有所不同)。正是凭借这套通用规则,计算机才能实现表达式的运算求值。这套规则是由运算操作符的三个性质决定:

  1. 操作符的优先级
  2. 操作符的结合性
  3. 操作符是否控制求值顺序

2.1 表达式运算符的优先级与结合性

当一个表达式存在多个运算符时,这些运算符操作数的计算先后会影响表达式最后的值。因此,需要一套统一的标准来确定运算符参与运算的先后顺序,而在确定顺序后,还存在着参与运算的运算符优先级相同的情况,在这种情况下,表达式运算顺序就取决于运算符的结合性。
各运算操作符具体性质,如下表:

注:

  1. 操作符优先级:从上至下,优先级由高到低
  2. N/A 无结合性,L-R 从左至右,R-L 从右至左
操作符描述用法示例结果类型结合性是否控制求值顺序
()聚组(表达式)与表达式相同N/A
()函数调用rexp (rexp,…,rexp)rexpL-R
[ ]下标引用rexp[rexp]lexpL-R
.访问结构成员lexp.member_namelexpL-R
->访问结构指针成员rexp->member_namelexpL-R
++后缀自增lexp ++rexpL-R
后缀自减lexp –rexpL-R
!逻辑反! rexprexpR-L
~按位取反~ rexprexpR-L
+单目,表示正值+ rexprexpR-L
-单目,表示负值- rexprexpR-L
++前缀自增++ lexprexpR-L
前缀自减– lexprexpR-L
*间接访问* rexplexpR-L
&取地址& lexprexpR-L
sizeof取其长度,以字节表示sizeof rexp sizeof(类型)rexpR-L
(类型)类型转换(类型) rexprexpR-L
*乘法rexp * rexprexpL-R
/除法rexp / rexprexpL-R
%整数取余rexp % rexprexpL-R
+加法rexp + rexprexpL-R
-减法rexp - rexprexpL-R
<<左移位rexp << rexprexpL-R
>>右移位rexp >> rexprexpL-R
>大于rexp > rexprexpL-R
>=大于等于rexp >= rexprexpL-R
<小于rexp < rexprexpL-R
<=小于等于rexp <= rexprexpL-R
==等于rexp == rexprexpL-R
!=不等于rexp != rexprexpL-R
&位与rexp & rexprexpL-R
^位异或rexp ^ rexprexpL-R
|位或rexp | rexprexpL-R
&&逻辑与rexp && rexprexpL-R
||逻辑或rexp || rexprexpL-R
? :条件操作符rexp ? rexp : rexprexpN/A
=赋值lexp = rexprexpR-L
+=以…加lexp += rexprexpR-L
-=以…减lexp -= rexprexpR-L
*=以…乘lexp *= rexprexpR-L
/=以…除lexp /= rexprexpR-L
%=以…取模lexp %= rexprexpR-L
<<=以…左移lexp <<= rexprexpR-L
>>=以…右移lexp >>= rexprexpR-L
&=以…与lexp &= rexprexpR-L
^=以…异或lexp ^= rexprexpR-L
|=以…或lexp |= rexprexpR-L
逗号rexp,rexprexpL-R

2.2 表达式求值的唯一计算路径

我们已经学习了运算操作符的相关知识,那么了解了这些知识后,就可以解决表达式求值的所有问题吗?

问题表达式,举例:
a * b + c * d + e * f
在如上的表达式计算中,我们能够确定唯一的计算顺序吗?
计算路径1:
在这里插入图片描述
计算路径2:在这里插入图片描述
以上的两种计算顺序都符合运算的规则,可是他们的计算结果都是一致的吗?
在a,b,c,e,d,f 都为变量时,两种计算路径的所得结果确实都相同,可是,当他们为表达式,函数时,并且他们执行后会互相影响时,两种不同的运算路径所得结果就会产生差异(即,在不同编译器上就会产生不同的结果),所以,在日常的编程中,我们应该注意并避免此种情况的发生。

//示例1:
int main()
{
	int i = 10;
	i = i-- - --i * ( i = -3 ) * i++ + ++i;
	printf("i = %d\n", i);
	
	return 0;
}

//示例2:
int fun()
{
  static int count = 1;
  return ++count;
}
//函数的调用先后顺序无法通过运算符的优先级确定
int main()
{
  int answer;
  answer = fun() - fun() * fun();
  printf( "%d\n", answer);
  
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值