C语言表达式求值

您所在的位置:网站首页 printf为什么不输出 C语言表达式求值

C语言表达式求值

2023-05-26 06:39| 来源: 网络整理| 查看: 265

表达式求值 一、整型提升1.什么是整型提升2.整型提升的意义3.如何进行整型提升4.证明整型提升确实存在 二、算数转换什么是算术转换 三、操作符的属性四、一些有问题的表达式

一、整型提升 我们学习操作符的目的就是为了将其用于表达式中求某个表达式的值表达式求值的顺序一部分是由操作符的优先级和结合性决定。 同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型(类型转换)类型转换又分为整型提升和算数转换不管是整型提升还是算术转换都只是在计算的瞬间进行的隐式转换,该变量实际的值不会发生变化 1.什么是整型提升

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

#include int main() { char a = 5; char b = 127; char c = a + b; printf("%d", c); }

在计算时,先将a和b转为int型,然后参与运算,计算出的结果在被截取后放入c中 在这里插入图片描述 结果为什么是-124呢,请继续往下看

2.整型提升的意义

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

其实上面就告诉我们两点

1.CPU内的整型运算器的操作数是4个字节,只要是小于4个字节都会被隐式转化为4字节进行计算(提供4字节不用白不用) 2.CPU很难实现两个小于int的整数相加,所以在运算前会转化为int,才能被CPU执行运算(CPU难以实现小于4字节的计算)

3.如何进行整型提升

整形提升是按照变量的数据类型的符号位来提升的(即符号位是几就补几) 整型提升只针对于char和short 再来看这个代码

#include int main() { char a = 5; char b = 127; char c = a + b; printf("%d", c); }

char a = 5; char在编译器中是有符号的,在计算机中如何存储呢? 整数原码反码补码都相同,计算机存储补码 5本来是:00000000000000000000000000000101(补码) 因为char为一个字节(8位),所以发生截断 a中的真实数据:00000101(补码) 同理b中的真实数据:01111111(补码) 我们将两者相加: 00000101(补码) 01111111(补码) 相加时会进行整型提升即: 00000000000000000000000000000101(补码) 00000000000000000000000001111111(补码) 这和上述没什么区别,但整型提升的过程是要有的 结果为:00000000000000000000000010000100(补码) 现在将结果放在c中发生截断 c中的真实数据:10000100(补码) 打印时%d是以十进制形式打印有符号整数,打印的是原码 因为要打印整数所以C发生整型提升 c为:11111111111111111111111110000100(补码) c的原码:100000000000000000000001111100(原码) 即-124

4.证明整型提升确实存在

eg1:

int main() { char a = 0xb6; short b = 0xb600; int c = 0xb6000000; if(a==0xb6) printf("a"); if(b==0xb600) printf("b"); if(c==0xb6000000) printf("c"); return 0; }

在这里插入图片描述 这里可以自己计算一下,a和b发生了整型提升,变为负数与原来的值不同所以只打印了c

eg2:

int main() { char c = 1; printf("%u\n", sizeof(c)); printf("%u\n", sizeof(+c)); printf("%u\n", sizeof(-c)); return 0; }

%u打印十进制无符号整数 在这里插入图片描述 这个案例更加清晰明了,c在参与运算时,进行整形提升所以算出来的字节为4字节

二、算数转换 什么是算术转换

在进行计算时,通常会隐式将低类型转化为高类型,再进行计算,计算完的结果是高类型,但原来的低类型不变,只是在计算的瞬间短暂提升.

long double double float unsigned long int long int unsigned int int 如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运 算。 警告: 算术转换要合理,要不然会有一些潜在的问题。 eg;

int main() { float f = 3.14; int c = 4; double d = 5.6; int num = f;//隐式转换,会有精度丢失 double s = c + d; printf("%d\n", num); printf("%lf\n", s); }

在这里插入图片描述

三、操作符的属性

复杂表达式的求值有三个影响的因素。

操作符的优先级操作符的结合性是否控制求值顺序 两个相邻的操作符先执行哪个? 取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

从上往下,分别是优先级的高低顺序.每个操作符后面有他们的结合性,是否控制求值顺序(有可能改变求值的顺序,比如逻辑与左边为假右边不执行)

四、一些有问题的表达式

并不是说满足了操作符的属性,就能够确定唯一的表达式的值

eg1:

int fun() { static int count = 1; return ++count; } int main() { int answer; answer = fun() - fun() * fun(); printf( "%d\n", answer);//输出多少? return 0; }

这样一段代码,不同的编译器会算出不同的结果,主要原因在于即使知道了优先级,但不清楚函数调用的先后顺序,就会算出不同的结果. 情况一:answer=2-34=-10 情况二:answer=4-23=-2 在VS2022中算出来是第一种情况

eg2:

#include int main() { int i = 1; int ret = (++i) + (++i) + (++i); printf("%d\n", ret); printf("%d\n", i); return 0; }

int ret = (++i) + (++i) + (++i);这个表达式的值也会导致编译器不知道如何去运算,不同的编译器会有不同的结果 情况一:ret=2+3+4=9 情况二:ret=4+4+4=12 即使知道了运算符++的优先级高于+但第一个 + 在执行的时候,第三个++是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个 + 和第 三个前置 ++ 的先后顺序 在这里插入图片描述 在这里插入图片描述 总结:为了避免问题表达式的书写,我们应加括号,确定运算的先后,或者避免写繁琐的代码,尽量分开写简单易懂的代码,让我们的表达式的计算路径唯一确定即可



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3