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

Posted 小赵小赵福星高照~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了操作符中表达式求值(隐式类型转换详解)以及操作符属性相关的知识,希望对你有一定的参考价值。

表达式求值


表达式求值的顺序一部分是由操作符的优先级和结合性决定。有些表达式的操作数在求值的过程中可能需要转换为其他类型。

隐式类型转换

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
	//00000000000000000000000000000011  a提升后
	char b = 127;
	//01111111
	//00000000000000000000000001111111  b提升后
	char c = a + b;//a和b要发生整形提升
	//00000000000000000000000010000010--相加后
    //存到c里面只能存放8个比特位:
	//10000010-c  c现在要%d整形打印,c要提升
    //c提升后:
    //11111111111111111111111110000010-补码
	//11111111111111111111111110000001
    //10000000000000000000000001111110
    //-126
	printf("%d\\n", c);
	return 0;
}

image-20210503161554502

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;
}

image-20210503161432461

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;
}

image-20210503161924632

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 <stdio.h>
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

以上是关于操作符中表达式求值(隐式类型转换详解)以及操作符属性的主要内容,如果未能解决你的问题,请参考以下文章

C语言隐式类型转换和算术转换详讲

C语言隐式类型转换和算术转换详讲

C语言二操作符详解(隐式类型转换之整型提升,算术转换)

C语言学习笔记详解操作符

C语言中的这个小细节你知道吗?

C语言操作符