操作符|操作符中表达式求值(隐式类型转换详解)以及操作符属性

表达式求值
文章目录

  • 表达式求值
    • 隐式类型转换
      • 1.整形提升
        • 整型提升的意义:
      • 2.算术转换
    • 操作符的属性
      • 一些问题表达式

表达式求值的顺序一部分是由操作符的优先级和结合性决定。有些表达式的操作数在求值的过程中可能需要转换为其他类型。
隐式类型转换 1.整形提升
偷偷的转换的,并不能直观的看到
C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整型提升的意义: 表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为int或unsigned int,然后才能送入CPU去执行运算。
int main() { char a = 3; //00000011 //00000000000000000000000000000011a提升后 char b = 127; //01111111 //00000000000000000000000001111111b提升后 char c = a + b; //a和b要发生整形提升 //00000000000000000000000010000010--相加后 //存到c里面只能存放8个比特位: //10000010-cc现在要%d整形打印,c要提升 //c提升后: //11111111111111111111111110000010-补码 //11111111111111111111111110000001 //10000000000000000000000001111110 //-126 printf("%d\n", c); return 0; }

操作符|操作符中表达式求值(隐式类型转换详解)以及操作符属性
文章图片

a和b都不够int大小,所以要进行整形提升,a和b的值被提升为普通整型,然后再执行加法运算。
加法运算完成之后,结果将被截断,然后再存储于c中。
如何整形提升?
整形提升是按照变量的数据类型的符号位来提升的。
//负数的整形提升 char c1 = -1; 变量c1的二进制位(补码)中只有8个比特位: 11111111 因为 char 为有符号的 char 所以整形提升的时候,高位补充符号位,即为1 提升之后的结果是: 11111111111111111111111111111111 //正数的整形提升 char c2 = 1; 变量c2的二进制位(补码)中只有8个比特位: 00000001 因为 char 为有符号的 char 所以整形提升的时候,高位补充符号位,即为0 提升之后的结果是: 00000000000000000000000000000001 //无符号整形提升,高位补0

整形提升的例子:
int main() { char a = 0xb6; //二进制序列:10110110 short b = 0xb600; //1011011000000000 int c = 0xb6000000; //10110110000000000000000000000000 if(a==0xb6)//比较大小也是运算,这里发生整形提升 printf("a"); if(b==0xb600)//比较大小也是运算,这里发生整形提升 printf("b"); if(c==0xb6000000)//c是整形,不用提升,就可以打印 printf("c"); return 0; }

操作符|操作符中表达式求值(隐式类型转换详解)以及操作符属性
文章图片

if判断语句里要进行比较大小运算,a是char类型,b是short类型,所以a和b要进行整形提升,整形提升后发生了改变,c不需要整形提升,所以可以打印
int main() { char c = 1; printf("%u\n", sizeof(c)); printf("%u\n", sizeof(+c)); printf("%u\n", sizeof(-c)); return 0; }

操作符|操作符中表达式求值(隐式类型转换详解)以及操作符属性
文章图片

sizeof括号中放的表达式是不参与运算的!我们假想c如果进行了运算,推算表达式所产生的类型是什么,发生整形提升,表达式的类型属性为int,所以为4个字节。
2.算术转换
比如一个int和一个long进行运算,那么这个int转换为long进行运算,小的转换成大的进行运算
操作符的属性 表达式的三个影响因素
  1. 操作符的优先级
    优先级在相邻两个操作符不一样时,看优先级
  2. 操作符的结合性
    在相邻两个操作符一样时,看结合性
  3. 是否控制求值顺序。
我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题
的。
一些问题表达式
代码1
//表达式的求值部分由操作符的优先级决定。 //表达式1 a*b + c*d + e*f

代码1在计算的时候,由于比+的优先级高,只能保证*的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行
a*b c*d a*b + c*d e*f a*b + c*d + e*f 或者: a*b c*d e*f a*b + c*d a*b + c*d + e*f

如果a、b、c、d、e、f是表达式,那么运算顺序不确定就是个大问题了。
代码2
//表达式2 c + --c;

同操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。
代码3
int main() { int i = 10; i = i-- - --i * ( i = -3 ) * i++ + ++i; printf("i = %d\n", i); return 0; }

这个代码在不同编译器的运行结果是不一样的。
代码4
int fun() { static int count = 1; return ++count; } int main() { int ret; ret = fun() - fun() * fun(); printf( "%d\n", ret); //输出多少呢? return 0; }

函数的调用先后顺序无法通过操作符的优先级确定
代码5
#include int main() { int i = 1; int ret = (++i) + (++i) + (++i); printf("%d\n", ret); printf("%d\n", i); return 0; } //尝试在linux 环境gcc编译器,VS2019环境下都执行,看结果。

Linux环境下编译结果为10
【操作符|操作符中表达式求值(隐式类型转换详解)以及操作符属性】vs2019环境下编译结果为12

    推荐阅读