目录
一、整型提升
C语言中,整型算术运算是在CPU的整型运算器上进行的。而整型运算器内操作数的字节长度一般是普通整型(即int)的字节长度,因此,整型运算时字节长度小于int的操作数都要在提升为普通整型后运算。
整型提升的规则:按照数据的符号位提升
例如:
#include<stdio.h>
int main()
{
char a = 127;
char b = 3;
char c = a + b;
printf("%d\n",c);
return 0;
}
由于a、b均属于char类型,在内存中占一个字节,因此:
a的补码:0111 1111
b的补码:0000 0011
按符号位整型提升后,
a的补码: 00000000000000000000000001111111
b的补码: 00000000000000000000000000000011
a+b得到:00000000000000000000000010000010
由于c属于char类型,进行截断,得到:
c的补码:1000010
c的反码:1000001
c的原码:11111110
c的值:-126
二、算术转换
当一个操作符的操作数数据类型不同时,需要其中一个操作数转换成另一个操作数的类型,否则运算就无法进行。这就是算术转换。
算术转换的方向:
int -> unsigned int -> long int -> unsigned long int -> float -> double -> long double
三、操作符属性
影响表达式求值有三个因素:
- 操作符优先级
- 操作符结合性
- 是否控制求值顺序
判断相邻操作符先执行哪个,要看操作符的优先级,比如我们从小学就知道的先乘除后加减;
如果操作符的优先级相同,就要看操作符的 结合顺序,是从左向右还是从右向左?例如下面:
#include<stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = (a++, --b, a + b); //逗号表达式的结合性是从左向右依次进行
printf("%d\n", c); //c = 3
return 0;
}
不过即使已经知道了操作符的优先级和结合性,我们也不一定能断定这个表达式没有问题。如下:
#include<stdio.h>
int main()
{
int a = 2;
int c = (a++) + (++a) + (++a);
printf("%d\n", c);
return 0;
}
在上面的代码中,我们已经预先知道它们的优先级后缀++ > 前缀++ > +,但我们却无法确定这个表达式是先算完a++,++a,++a,最后再相加,还是先算a++,++a的和,再算后面的,由于这个表达式的计算路径不是唯一的,所以这个表达式是有问题的。
关于操作符是否控制求值顺序,只有三个操作符可以控制求值顺序:
- 逻辑操作符与(&&):(exp1) && (exp2),当exp1为假时,整个表达式为假,不执行exp2,直接进行后面的操作;
- 逻辑操作符或(| |):(exp1) || (exp2),当exp1为真时,整个表达式为真,不执行exp2;
- 条件操作符:(exp1) ? (exp2) :(exp3),当exp1为真时,执行exp2;否则,执行exp3.
四、总结
C语言中的表达式求值,就要注意两个方面:一是数据类型的转换,包括整型提升、算数转换;二是操作符的属性,看操作符的优先级和结合性等。通过操作符的属性,我们如果不能确定表达式有唯一计算路径,那么这个表达式就是有问题的。