鹏哥C语言72---操作符与表达式求值
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
//---------------------------------------------------------------------------------------------------------------操作符-12. 表达式求值
//表达式求值的顺序一部分是由 操作符 的优先级和结合性 决定。
//同样, 有些表达式的操作数在求值的过程中可能需要转换为其他类型。
//-------------------------------------------------------------------------------------------------------12.1隐式类型转换
//C的整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。
//为了获得这个精度, 表达式中的字符和短整型操作数在使用之前被转换为普通整型, 这种转换称为整型提升。
int main()
{char a = 5;char b = 126;char c = a + b; //运算前需要先把 a,b整型提升为 int 类型printf("%d\n", c);return 0;
}
整型提升的意义 :
表达式的整型运算要在CPU的相应运算器件内执行, CPU内整型运算器(ALU)的操作数的字节长度,
一般就是int的字节长度, 同时也是CPU的通用寄存器的长度。
因此, 即使两个char类型的相加, 在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以, 表达式中各种长度可能小于int长度的整型值, 都必须先转
换为int或unsigned int, 然后才能送入CPU去执行运算。
如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的
//-------------------------------------------------负数的整形提升
char c1 = -1
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//-------------------------------------------------正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//------------------------------------------------无符号数整型提升,高位补0
//-----------------------------------------------------------------------整型提升举例1
int main()
{char a = 5;//5的2进制序列,4个字节,32bit//00000000000000000000000000000101 5的补码//char a---------------00000101char b = 126;//126的2进制序列,4个字节,32bit//00000000000000000000000001111110 126的补码//char b--------------01111110char c = a + b; //char a---------------00000101//char b---------------01111110//表达式中的字符和短整型操作数在使用之前被转换为普通整型//00000000000000000000000000000101---a//00000000000000000000000001111110---b//00000000000000000000000010000011---c//char c---------------10000011//负数整型提升//11111111111111111111111110000011---c的补码//转为原码//10000000000000000000000001111101---- -125printf("%d\n", c); // -125return 0;
}
//-----------------------------------------------------------------------整型提升举例2
int main()
{char a = 0xb6;short b = 0xb600;int c = 0xb6000000;if (a == 0xb6) // a整型提升了printf("a"); //不打印if (b == 0xb600) // b整型提升了printf("b"); //不打印if (c == 0xb6000000) //c 不需要整型提升printf("c"); //打印return 0;
}
//-----------------------------------------------------------------------整型提升举例3
int main()
{char c = 1;printf("%u\n", sizeof(c));//1printf("%u\n", sizeof(+c));//4printf("%u\n", sizeof(-c)); //4return 0;}
//-------------------------------------------------------------------------------------------------------12.2 算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类
型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
long double
double
float
unsigned long int
long int
unsigned int
int
//如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
//但是算术转换要合理,要不然会有一些潜在的问题。
//-------------------------------------------------------------------------------------------------------12.3 操作符的属性
复杂表达式的求值有三个影响的因素。
1.操作符的优先级
2.操作符的结合性
3.是否控制求值顺序
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
//操作符优先级
//表格
//-------------------------------------------------------------------问题表达式/代码
int main()
{3 + 2 * 4 + 5;//优先级只在相邻操作符中考虑3 * 4 + 5 * 2 + 8 * 5; //有多种求值路径----------是问题表达式 1int c = 2;c+--c; //------------------------------问题表达式 2int i = 10;i = i-- - --i * (i = -3) * i++ + ++i; //---------- 问题表达式 3 printf("i =%d\n", i);return 0;
}
//----------------------------------------问题代码 4
int fun()
{static int count = 1;return ++count; //由于 static,每次进入函数,count都不会销毁,每次调用函数,都会返回不同的count值
}
int main()
{int answer;answer = fun() - fun() * fun();printf("%d\n", answer);//输出多少?return 0;
}
// ----------------------------------------问题代码 5
int main()
{int i = 1;int ret = (++i) + (++i) + (++i); //求解路径不唯一printf("%d\n", ret); // b是多少?printf("%d\n", i); // i是多少?//不同编译器/服务器下,答案不同//因为求值路径不唯一,不同不同编译器/服务器选择的计算路径不同return 0;
}
//总结:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。