目录
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
1)隐式类型转换
C的整型算术运算总是至少以缺省整型类型的精度来进行的。(缺省:默认的意思)
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。 因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。 通用CPU(general-purpose CPU)是难以直接实现两个8比特字节(两个char型)直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的
#include<stdio.h>
int main()
{
char a = -1;//-1是整数,32个比特位
//10000000 00000000 00000000 00000001 - -1原码
//11111111 11111111 11111111 11111110 - -1反码
//11111111 11111111 11111111 11111111 - -1补码
//char c只有8个比特位,放不下这么多
//所以c里面只能放下 - 11111111
//
//c的类型是char c,char c是有符号char,意味着在上面的二进制里面(11111111)
//最高位的1把它解读为符号位,符号1表示它是负数,所以进行整数提升时,是拿它的符号位进行提升的
//
//11111111 - c 假设要对c的值进行整型提升
//c的符号位是1,提升之后变成
//11111111 11111111 11111111 11111111(补完之后提升成一个整型)
return 0;
}
//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0
有符号整型提升:高位补充符号位;无符号整型提升:高位补0。
例子1:
#include<stdio.h>
int main()
{
//我们一般把字符类型归纳在整型家族里,因为字符类型存储的ASCII码值
//ASCII码值是整数,只要是整型家族中的成员,在内存中存储的都是它的补码
//有符号的char能表示的范围是-128~127(取值范围)
//ASCII的取值范围是0~127
char a = 5;
char b = 126;
char c = a + b;
printf("c=%d\n", c);
return 0;
}
求c的值?(c=-125)
//int - 4byte - 32bit
//char - 1byte - 8bit
#include<stdio.h>
int main()
{
char a = 5;
//00000000 00000000 00000000 00000101 5
//只存8个比特位 00000101
char b = 126;
//00000000 00000000 00000000 01111110
//只存8个比特位 01111110
char c = a + b;
//00000000 00000000 00000000 00000101 - a
//00000000 00000000 00000000 01111110 - b
//00000000 00000000 00000000 10000011
//10000011 - c
printf("c=%d\n", c);//-125
//当我们要打印的时候,%d意味着我们要打印一个整数
//c是char型,所以我们要发生整型提升
//10000011 - c
//提升后
//11111111 11111111 11111111 10000011(补码)
//11111111 11111111 11111111 10000010(反码)
//10000000 00000000 00000000 01111101(原码)
return 0;
}
例子2:
#include<stdio.h>
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;
}
求打印的结果?(c)
#include<stdio.h>
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if (a == 0xb6)//a即使是跟0xb6比较大小,也是会发生整型提升
//a发生整型提升后,结果可能跟0xb6不一样
printf("a");
if (b == 0xb600)//b和a一样
printf("b");
if (c == 0xb6000000)
printf("c");//所以程序运行结果只打印c
return 0;
}
实例1中的a,b要进行整形提升,但是c不需要整形提升
a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,
但是c不发生整形提升,则表 达式 c==0xb6000000 的结果是真.
所以程序输出的结果是: c
如果将char型改为unsigned,则会打印出abc。
因为无符号整型提升高位补0,最后的结果跟不提升的结果一致。
#include<stdio.h>
int main()
{
unsigned char a = 0xb6;
unsigned short b = 0xb600;
int c = 0xb6000000;
if (a == 0xb6)
printf("a");
if (b == 0xb600)
printf("b");
if (c == 0xb6000000)
printf("c");
return 0;
}
例子3:
int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0;
}
求打印的结果?(1,4,4)
c只要参与表达式运算,就会发生整形提升.
表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字 节.
表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节.
但是 sizeof(c) ,就是1个字节.
#include<stdio.h>
int main()
{
char c = 1;
printf("%u\n", sizeof(c));//1
printf("%u\n", sizeof(+c));//4 因为它是整型提升之后参与运算
printf("%u\n", sizeof(-c));//4 所以打印结果是4
return 0;
}
以上讲的是,一个表达式里面它的类型如果达不到整型大小的char,short,计算的时候就要进行整型提升,如果一个表达式的大小已经超过一个整型呢?比如float,long long。那应该如何处理?这时要用到算术转换了。
2)算术转换
以上讲的整型提升,讨论的是大小小于整型的这种类型,它要发生整型提升;如果它的大小大于或等于整型大小的整型类型,它们在使用的时候也会进行转换,这种转换称为算术转换。
3)操作符的属性
复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?
取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
逻辑与(&&)运算符,当左边为假,右边不要算;左边为真,右边要算。
逻辑或( | | )运算符,当左边为真,右边不要算;左边为假,右边要算。
条件操作符( ? : ),当表达式1为真,表达式2算,表达式1为假,表达式3算。
逗号表达式(,),会从左到右进行依次计算,真正起到判断作用的是最后的表达式。
这些操作符会影响求值的求值顺序。
一些问题表达式
注释:代码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
注释:同上,操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。
表达式3在不同编译器中测试结果:非法表达式程序的结果
这个代码有没有实际的问题?
有问题! 虽然在大多数的编译器上求得结果都是相同的。
但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:
先算乘法, 再算减法。 函数的调用先后顺序无法通过操作符的优先级确定。
虽然编译器运算的结果为12,但12这个结果不一定是对的。在不同编译器运行的结果可能不是12。