整型提升
C语言中整型算术运算总是**至少以**`**int**`**整型类型的精度**来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是**int**的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU(general-purpose CPU)是难以直接实现两个字节直接相加运算(虽然机器指令中能有这种字节相加指令)。所以,表达式中各种长度可能小于**int**长度的整型值,都必须先转换为**int**或**unsigned int**,然后才能送入CPU去执行运算。
如何进行整体提升:
- 有符号整数提升:是按照变量的数据类型的符号位来提升的
- 无符号整数提升:高位补0
//负数的整型提升
char c1 = -1;
//11111111——c1的补码
//11111111 11111111 11111111 11111111——整型提升后c1的补码
//正数的整型提升
char c1 = 1;
//00000001——c1的补码
//00000000 00000000 00000000 00000001——整型提升后c1的补码
#include<stdio.h>
int main()
{
char c1 = 5;
char c2 = 126;
//00000101——c1的补码
//01111110——c2的补码
//00000000 00000000 00000000 00000101——整型提升后c1的补码
//00000000 00000000 00000000 01111110——整型提升后c2的补码
//00000000 00000000 00000000 10000011——相加
//10000011——c3的补码
//11111101——c3的原码—— -125
char c3 = c1 + c2;
printf("%d", c3);//输出 -125
return 0;
}

算术转换
如果某个操作符的**各个操作数属于不同的类型**,那么**除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行**。下面的层次体系称为寻常算术转换:
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名靠后,那么首先要转换为另外一个操作数的类型后执行运算。
float a;
double b;
b = b + a;//a 先转换为 double类型,再计算
问题表达式解析
表达式1
a * b + c * d + d * e;
表达式1在计算的时候,由于*比+的优先级高,只能保证,第一个* 的计算是比第一个+ 早,但是优先级并不能决定第三个* 比第一个+ 早执行。
表达式2
c + -- c
操作符的优先级只能决定-- 的运算在+ 的运算的前面,但是我们并没有办法得知,+ 操作符的左操作数的获取在右操作数之前还是之后,所以结果是不可预测的,是有歧义的。
表达式3
#include<stdio.h>
int fun()
{
static int i = 1;
return ++i;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf("%d", answer);
return 0;
}
虽然这串代码在大多数的编译器上求得结果都是相同的。
但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法,再算减法。函数的调用先后顺序无法通过操作符的优先级确定。
表达式 4
#include<stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d", ret);
printf("%d", i);
return 0;
}
这段代码中的第一个+ 在执行的时候,第三个++是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个+ 和第三个前置++ 的先后顺序。
总结
即使有了操作符的优先级和结合性,我们写出的表达式依然有可能不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在潜在风险的,建议不要写出特别负责的表达式。
2万+

被折叠的 条评论
为什么被折叠?



