本章主要讲解运算符的优先级和结合顺序
算术运算符 | + - / % |
关系运算符 | > < == >= <= != |
逻辑运算符 | ! && || |
位运算符 | << >> ~ | ^ & |
赋值运算符 | =及其扩展赋值运算符 |
条件运算符 | ?: |
逗号运算符 | , |
指针运算符 | * & |
求字节运算符 | sizeof() |
强制类型转换运算符 | (要转换的类型)值 |
分量运算符 | .-> |
下标运算符 | [] |
其他运算符 | 其他函数调用运算符 |
优先级见后面
比如:if(3这种是不行的,C语言不支持这种连续判断,所以会直接跳过这个判断直接执行下面的。
举例:
if (3 < a < 10) //如果要判断3
printf("a is right\n") ;
else {
printf(" a is wrong' );}
在上述的例子中无论 a 输入什么值, 都是输出 a is right ;
不能直接用== 判断浮点数是否相等
在C语言中,浮点数由于存在精度误差,不能直接用== 来判断两个浮点数是否相等。这是因为在计算机内部,浮点数都是以二进制表示的,在转换为十进制时会有一定的精度损失。因此,如果直接用 ==来比较两个浮点数是否相等,很可能会因为精度误差导致判断错误。
举例:
//判断两个浮点数是否相等,
float f= 234. 56;
if (f == 234.56)
{
printf("f is equal to 234. 56\n") ;
else {
printf("f is not equal to 234. 56\n") ;}
正确的做法是使用一个容错范围来判断两个浮点数的差值是否小于该范围。比如,我们可以使用下面的代码来判断两个浮点数是否相等:
//判断两个浮点数是否相等,必须用下面的方法
float f = 234. 56;|
if (f - 234.56>-0.0001 && f-234.56<0. 0001)
printf("f is equal to 234. 56\n");}
else {
printf("f is not equal to 234. 56\n") ;}
布尔型值(或布尔值)用于表示逻辑真假值
,通常用来进行逻辑运算。在很多编程语言中,布尔型值 真值通常表示为 1,假值则表示为 0
。不过,这个映射关系并不是绝对的,具体应该怎么表示布尔值真假值取决于编程语言的实现。int j = 10 , int i = !! j
; i 的最后的值并不是负负得正为10 而是 1!!j 表示将 j 的值转换成布尔类型,然后再取反。
因为 j 的值是 10,而在计算机中,非零整数值都被视为 true
,因此 !!j 的值就是 true。取反后就得到 false,而在 C++ 中,false 的整数值为 0,所以 int i = !!j 的最终结果就是 1。true
为了理解有些操作符存在的限制,必须理解左值(L-value) 和右值(R-value) 之间的区别.
举例: 右赋值
a=b+25;
其中,a是一个左值,因为它标识了一个可以存储结果值的地点; b+25是-一个右值,因为它指定了一个值。
这就是一个正常的把b+25的结果赋值给a的运算。
举例:左赋值
b+25=a;
因为每个位置都包含一个值, 所以原先用作左值的a此时也可以作为右值;然而,b + 25不能作为左值,因为它并未标识1个特定的位置(并不对应特定的内存空间)。因此,上面这条赋值语句是非法的。
而且赋值运算符的优先级很低,仅仅高于逗号运算符。
条件运算符是C语言中唯一的一种三目运算符。
条件运算符是C语言中唯一的一种三目运算符。
三目运算符代表有三个操作数;
双目运算符代表有两个操作数,如逻辑与运算符&&就是双目运算符;
单目运算符代表有一个操作数,如逻辑非运算符就是单目运算符。运算符也称操作符.
优先级仅高于赋值和逗号运算符
?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
条件运算符(也称三元运算符)是一种简洁的运算符,用于在确定一个条件为真或假时选择不同的值。它通常被称为“三元运算符”,因为它接受三个操作数,如下所示:
condition ? value1 : value2
这里,condition 是一个布尔表达式,如果为真,则条件运算符返回 value1;如果为假,则返回 value2。例如,以下语句将 x 设置为 10,如果 y 大于 5,否则设置为 20:
x = y > 5 ? 10 : 20;
这个语句的意思是:如果 y 大于 5,则将 x 设置为 10,否则将 x 设置为 20。
条件运算符可以帮助我们编写简洁易读的代码,而不需要使用复杂的 if-else 语句。它可以让我们快速、简单地执行条件判断,并选择不同的值。
优先级最低!
, | 逗号运算符 | 表达式,表达式,… | 左到右 | 从左向右顺序运算 |
逗号运算符是在 C 和 C++ 等编程语言中的一种运算符。它的作用是在两个表达式之间插入一个逗号,并计算这两个表达式。这两个表达式的值会被忽略,逗号运算符的结果值是它的第二个操作数的值
。
int x = 5, y = 10, z;
z = (x++, y++);
在这个例子中,x 和 y 都会自增 1,但它们的值不会被使用。z 的值将是 y 在自增之后的值,即 11。
通过逗号运算符,我们可以先做一些准备操作
比如下面:最终while循环是否结束取决于scanf(" %d%d%d" ,&a,&b,&c)!=EOF这个表达式的真假
while (fflush (stdin), scanf (" %d%d%d" , &a, &b, &c) !=EOF)
如果是一个表达式,需要看最后一个表达式的真假。
举例:
int i=1;
int s=i++;
printf("%d\n",s); //后缀++,先s=i,再i+1,输出s等于=1
int i2=1;
int d=++i2;
printf("%d\n",d); //++前缀,先i+1,再d=i2,输出s等于i2+1=2
举例:
int i=-1;
int j;
j=i++>-1; //后++,或者后--; 等价于j=i>-1;i=i+1;
printf("i=%d, j=%d\n", i, j); //i=0, j=0(0是错误表示,表示不成立)
j=!++i;//当前加加时,直接按优先级结合顺序. ++的优先级跟!同级别,按照右到左的结合顺序,先++i,再!i
printf("i=%d,j=%d,sizeof(i)=%d\n", i, j, sizeof(i)); //i=++0=1; j=!1=0; sizeof(i)是求字节数的,i是个整数1,字节数为4个字节
5++
这种是错误的。
//5++://如果运行该句,会造成编译不通
比如:
int i=1;
i++; //他自己是一个表达式,那么结果最后i肯定是2
字节运算符是一种运算符,它们可以对一个或多个二进制位进行操作
。
例如,如果要对两个整型变量 x 和 y 进行按位与运算,可以使用下面的语句:
Copy code
int result = x & y;
这个语句将计算 x 和 y 的按位与,并将结果存储在 result 变量中。
此外,还可以使用位操作符来检查一个整型变量的特定二进制位是否被置位。例如,要检查 x 变量的第 3 位是否被置位,可以使用如下语句:
Copy code
if (x & (1 << 3)) {
// 第 3 位被置位
}
在这个语句中,1 被左移 3 位,然后与 x 进行按位与运算。如果 x 的第 3 位被置位,那么结果将是一个非零数,表示条件成立,否则结果将是零,表示条件不成立。
类型转换栈溢出
是指当程序试图将 一个大型数据类型转换为较小的数据类型时,可能会发生的错误
。例如,将一个 long 类型的数据强制转换为 int 类型的数据时,如果 long 类型的数据超出了 int 类型的取值范围,则可能会发生类型转换栈溢出错误。
例如,如果整型的取值范围是 − 2 31 -2^{31} −231 到 2 31 − 1 2^{31}-1 231−1,那么将一个大于 2 31 − 1 2^{31}-1 231−1 的数字强制转换为整型就会导致溢出。
为了解决类型转换栈溢出问题,需要遵守一些基本的编程原则和技巧。例如:
尽量避免使用强制类型转换,特别是将大型数据类型转换为较小的数据类型。如果确实需要使用强制类型转换,则应当检查转换后的值是否超出了目标类型的取值范围,并在必要时进行特殊处理。
尽量使用更宽容的数据类型,例如 long 或 double,来存储数据。这样可以避免因类型转换栈溢出而导致的数据丢失。
在编写程序时,应当注意程序的结构和逻辑,尽量避免出现死循环和其他导致程序崩溃的错误。
如果在程序运行过程中发现类型转换栈溢出错误,则应当及时修复错误并重新测试程序。
如果程序需要处理大量数据,则应当考虑使用更高级的编程语言或框架,例如 Java 或 C++,来提高程序的运行效率和稳定性。
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
|
() |
圆括号 |
(表达式)/函数名(形参表) |
|||
. |
成员选择(对象) |
对象.成员名 |
|||
-> |
成员选择(指针) |
对象指针->成员名 |
|||
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
(类型) |
强制类型转换 |
(数据类型)表达式 |
|||
++ |
自增运算符 |
++变量名/变量名++ |
单目运算符 |
||
-- |
自减运算符 |
--变量名/变量名-- |
单目运算符 |
||
* |
取值运算符 |
*指针变量 |
单目运算符 |
||
& |
取地址运算符 |
&变量名 |
单目运算符 |
||
! |
逻辑非运算符 |
!表达式 |
单目运算符 |
||
~ |
按位取反运算符 |
~表达式 |
单目运算符 |
||
sizeof |
长度运算符 |
sizeof(表达式) |
|||
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
* |
乘 |
表达式*表达式 |
双目运算符 |
||
% |
余数(取模) |
整型表达式/整型表达式 |
双目运算符 |
||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
- |
减 |
表达式-表达式 |
双目运算符 |
||
5 |
<< |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
>> |
右移 |
变量>>表达式 |
双目运算符 |
||
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
>= |
大于等于 |
表达式>=表达式 |
双目运算符 |
||
< |
小于 |
表达式<表达式 |
双目运算符 |
||
<= |
小于等于 |
表达式<=表达式 |
双目运算符 |
||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
!= |
不等于 |
表达式!= 表达式 |
双目运算符 |
||
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
13 |
?: |
条件运算符 |
表达式1? 表达式2: 表达式3 |
右到左 |
三目运算符 |
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
|
/= |
除后赋值 |
变量/=表达式 |
|||
*= |
乘后赋值 |
变量*=表达式 |
|||
%= |
取模后赋值 |
变量%=表达式 |
|||
+= |
加后赋值 |
变量+=表达式 |
|||
-= |
减后赋值 |
变量-=表达式 |
|||
<<= |
左移后赋值 |
变量<<=表达式 |
|||
>>= |
右移后赋值 |
变量>>=表达式 |
|||
&= |
按位与后赋值 |
变量&=表达式 |
|||
^= |
按位异或后赋值 |
变量^=表达式 |
|||
|= |
按位或后赋值 |
变量|=表达式 |
|||
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
从左向右顺序运算 |
说明:
同一优先级的运算符,运算次序由结合方向所决定。
简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符
从上述表中可以大致归纳出各类运算符优先级:
初等运行符->单目运算符->算术运算符->关系运算符->逻辑运算符->条件运算符->赋值运算符->逗号运算符
初等单目一二级, // 初等运算符和单目运算符分别是第1、2优先级
乘除求余加减移, // 这句里面的运算符全归为算术运算符,移表示移位
关系等于不等于, // 关系运算符(< <= > >=)
按位与来异或或, // 位运算符优先级顺序: & -> ^ -> |
逻辑与或条件弱, // 逻辑运算符优先级顺序: && -> ||,后面跟着优先级比较低(弱)的条件运算符
赋值逗号一点破。 // 赋值,逗号最低
参考:
C语言运算符优先级 之 快速记忆