当前位置: 首页 > article >正文

C 语言运算符的优先级和结合性

运算符的结合性和优先级

优先级运算符描述结合性
1()
[]
->
.
函数调用、数组下标、结构体 / 联合体成员通过指针访问、结构体 / 联合体成员访问从左到右
2!
~
++ (前缀)
-- (前缀)
+ (一元)
- (一元)
* (间接寻址)
& (取地址)
sizeof
(type)
逻辑非、按位取反、前缀自增、前缀自减、一元正号、一元负号、间接寻址、取地址、获取操作数的大小、类型转换从右到左
3*
/
%
乘法、除法、取模从左到右
4+
-
加法、减法从左到右
5<<
>>
左移、右移从左到右
6<
<=
>
>=
小于、小于等于、大于、大于等于从左到右
7==
!=
相等、不相等从左到右
8&按位与从左到右
9^按位异或从左到右
10<code>|</code>按位或从左到右
11&&逻辑与从左到右
12<code>||</code>逻辑或从左到右
13? :条件运算符(三元运算符)从右到左
14=
+=
-=
*=
/=
%=
<<=
>>=
&=
^=
<code>|=</code>
赋值、复合赋值运算符从右到左
15,逗号运算符从左到右

上手demo

#include <stdio.h>

int main() {
    int a = 2, b = 3, c = 4;

    // 乘法优先级高于加法
    int result1 = a + b * c;  // 先计算 b * c,结果为 12,再加上 a,最终结果为 14
    printf("result1: %d\n", result1);  

    // 使用括号改变优先级
    int result2 = (a + b) * c;  // 先计算 a + b,结果为 5,再乘以 c,最终结果为 20
    printf("result2: %d\n", result2);  

    // 后缀自增和前缀自增
    int d = 5;
    int result3 = d++ + ++d;  // 先使用 d 的值 5 进行计算,然后 d 自增,再将自增后的 d(此时为 7)加 1,最终结果为 12
    printf("result3: %d\n", result3);  

    // 逻辑运算符
    int result4 = (a < b) && (b < c);  // 先计算 a < b 和 b < c,结果都为真,最终结果为 1
    printf("result4: %d\n", result4);  

    // 条件运算符
    int result5 = (a > b)? a : b;  // a 不大于 b,所以结果为 b 的值,即 3
    printf("result5: %d\n", result5);  

    // 复合赋值运算符
    a += b;  // 等价于 a = a + b,a 的结果为 5
    printf("a after +=: %d\n", a);  

    return 0;
}

括号运算符 ()

用于改变运算的优先级,也用于函数调用和类型转换,其在表达式中,括号内的运算总是优先进行。

#include <stdio.h>

int main() {
    int a = 5, b = 3, c = 2;
    // 改变运算顺序
    int result1 = (a + b) * c;  // 先计算 a + b,结果为 8,再乘以 c,最终结果为 16
    printf("result1: %d\n", result1);  

    // 函数调用
    int sum(int x, int y) {
        return x + y;
    }
    int result2 = sum(a, b);  // 调用 sum 函数,将 a 和 b 的值传递给函数,结果为 8
    printf("result2: %d\n", result2);  

    // 类型转换
    float f = (float)a;  // 将整型 a 转换为浮点型,结果为 5.0
    printf("result3: %f\n", f);  

    return 0;
}

数组下标运算符 []

用于访问数组中的元,其下标必须是整数,可以是常量或变量。

*越界访问数组(使用超出数组长度的下标)会导致未定义行为!!!

#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int index = 2;
    int element = arr[index];  // 访问数组 arr 中索引为 2 的元素,结果为 30
    printf("element: %d\n", element);  

    return 0;
}

结构体和联合体成员访问运算符 . 和 ->

  1. . 用于直接访问结构体或联合体的成员。
  2. -> 用于通过指针访问结构体或联合体的成员。
#include <stdio.h>
#include <stdlib.h>

struct Point {
    int x;
    int y;
};

int main() {
    struct Point p = {10, 20};
    // 直接访问成员
    int x = p.x;  // 访问结构体 p 的成员 x,结果为 10
    printf("x: %d\n", x);  

    struct Point *ptr = (struct Point *)malloc(sizeof(struct Point));
    ptr->x = 30;  // 通过指针访问成员 x
    ptr->y = 40;  // 通过指针访问成员 y
    printf("ptr->x: %d\n", ptr->x); 
    printf("ptr->y: %d\n", ptr->y);  

    free(ptr);  

    return 0;
}

 一元运算符

逻辑非 !

对操作数取逻辑反,将非零值变为 0,当操作数 0 返回结果为 1。

#include <stdio.h>

int main() {
    int a = 0;
    int result =!a;  // 结果为 1
    printf("result: %d\n", result);  

    return 0;
}

按位取反 ~

对操作数的二进制表示按位取反,也包括符号位(0为正,1为负)

#include <stdio.h>

int main() {
    int a = 5;  // 二进制: 0101
    int result = ~a;  // 结果为 -6(二进制: 1010)
    printf("result: %d\n", result);  

    return 0;
}

前缀 / 后缀自增 ++ 和前缀 / 后缀自减 --

  1. 前缀:将操作数加 / 减 1,再使用结果。
  2. 后缀:先使用操作数的值,将操作数加 / 减 1。
#include <stdio.h>

int main() {
    int a = 5;
    int b = ++a;  // 前缀自增,a 先加 1 变为 6,b 为 6
    int c = a++;  // 后缀自增,使用 a 的值 6,然后 a 加 1 变为 7
    printf("b: %d, c: %d, a: %d\n", b, c, a);  

    return 0;
}

一元正号 + 和一元负号 -

  1. +:不改变操作数的值,主要用于明确表示正数
  2. -:对操作数取负
#include <stdio.h>

int main() {
    int a = 5;
    int b = -a;  // 结果为 -5
    int c = +a;  // 结果为 5
    printf("b: %d, c: %d\n", b, c);  

    return 0;
}

间接寻址 * 和取地址 &

  1. *:解引用指针,获取指针访问所指向的
  2. &:获取变量的地址
#include <stdio.h>

int main() {
    int a = 10;
    int *ptr = &a;  // 获取 a 的地址存储在 ptr 中
    int value = *ptr;  // 通过 ptr 访问 a 的值,结果为 10
    printf("value: %d\n", value);  

    return 0;
}

sizeof 运算符

获取操作数的大小(以字节为单位)。

#include <stdio.h>

int main() {
    int a;
    size_t size = sizeof(a);  // 获取 int 类型的大小,通常为 4
    printf("size: %zu\n", size);  

    return 0;
}

类型转换 (type)

将操作数转换为指定的数据类型。当是多精度转单精度类型时,可能导致数据丢失(如浮点数转整数)或截断。

#include <stdio.h>

int main() {
    double d = 3.14;
    int i = (int)d;  // 将双精度浮点数转换为整数,结果为 3
    printf("i: %d\n", i);  

    return 0;
}

算术运算符

乘法 * 和除法 /

  1. *:两个操作数相乘。
  2. /:两个操作数相除,对于整数除法,结果会舍去小数部分;对于浮点数除法,使用 float 或 double 类型。
#include <stdio.h>

int main() {
    int a = 10, b = 3;
    int product = a * b;  // 结果为 30
    int quotient = a / b;  // 结果为 3(整数除法)
    printf("product: %d, quotient: %d\n", product, quotient);  

    return 0;
}

取模 %

计算两个整数相除的余数。操作数必须是整数,否则会导致编译错误!!!

#include <stdio.h>

int main() {
    int a = 10, b = 3;
    int remainder = a % b;  // 结果为 1
    printf("remainder: %d\n", remainder);  

    return 0;
}

加法 + 和减法 -

  1. +:两个操作数相加。
  2. -:两个操作数相减。
#include <stdio.h>

int main() {
    int a = 5, b = 3;
    int sum = a + b;  // 结果为 8
    int difference = a - b;  // 结果为 2
    printf("sum: %d, difference: %d\n", sum, difference);  

    return 0;
}

移位运算符

左移 <<

将操作数的二进制表示向左移动指定的位数,右边补 0。

#include <stdio.h>

int main() {
    int a = 5;  // 二进制: 0101
    int result = a << 2;  // 左移 2 位,结果为 20(二进制: 10100)
    printf("result: %d\n", result);  

    return 0;
}

右移 >>

将操作数的二进制表示向右移动指定的位数,对于无符号数左边补 0,对于有符号数根据编译器和系统可能补 0 或符号位。

#include <stdio.h>

int main() {
    int a = 10;  // 二进制: 1010
    int result = a >> 1;  // 右移 1 位,结果为 5(二进制: 0101)
    printf("result: %d\n", result);  

    return 0;
}

 关系运算符:<(小于)、<=(小于等于)、>(大于)、>=(大于等于)、==(相等)、!=(不相等)

用于比较两个操作数的大小或相等性,结果为 1(真)或 0(假)。

#include <stdio.h>

int main() {
    int a = 5, b = 3;
    int less_than = a < b;  // 结果为 0(假)
    int less_than_or_equal = a <= b;  // 结果为 0(假)
    int greater_than = a > b;  // 结果为 1(真)
    int greater_than_or_equal = a >= b;  // 结果为 1(真)
    int equal = a == b;  // 结果为 0(假)
    int not_equal = a!= b;  // 结果为 1(真)
    printf("less_than: %d, less_than_or_equal: %d, greater_than: %d, greater_than_or_equal: %d, equal: %d, not_equal: %d\n", 
           less_than, less_than_or_equal, greater_than, greater_than_or_equal, equal, not_equal);  

    return 0;
}

位运算符

  1. 位运算符用于对二进制位进行操作,常用于底层编程、硬件控制和性能优化。
  2. 按位与 & 常用于清除某些位或检查特定位是否置位。
  3. 按位或 | 常用于设置某些位。
  4. 按位异或 ^ 常用于反转某些位或交换两个数的值(不使用临时变量)。

按位与 &

对操作数的每一位进行逻辑与操作,都为 1 时结果为 1,否则为 0。

#include <stdio.h>

int main() {
    int a = 5;  // 二进制: 0101
    int b = 3;  // 二进制: 0011
    int result = a & b;  // 结果为 1(二进制: 0001)
    printf("result: %d\n", result);  

    return 0;
}

按位异或 ^

对操作数的每一位进行逻辑异或操作,不同为 1,相同为 0。

#include <stdio.h>

int main() {
    int a = 5;  // 二进制: 0101
    int b = 3;  // 二进制: 0011
    int result = a ^ b;  // 结果为 6(二进制: 0110)
    printf("result: %d\n", result);  

    return 0;
}

按位或 |

对操作数的每一位进行逻辑或操作,有一个为 1 时结果为 1,否则为 0。

#include <stdio.h>

    int a = 5;  // 二进制: 0101
    int b = 3;  // 二进制: 0011
    int result = a | b;  // 结果为 7(二进制: 0111)
    printf("result: %d\n", result);  

    return 0;
}

逻辑运算符

逻辑与 && 和逻辑或 || 具有短路特性,即对于 &&,如果第一个操作数为假,则不计算第二个操作数;对于 ||,如果第一个操作数为真,则不计算第二个操作数。

逻辑与 &&

当且仅当两个操作数都为真(非零)时,结果为真。

#include <stdio.h>

int main() {
    int a = 5, b = 0;
    int result = (a > 0) && (b > 0);  // 结果为 0(假)
    printf("result: %d\n", result);  

    return 0;
}

逻辑或 ||

当两个操作数中至少有一个为真时,结果为真。

#include <stdio.h>

int main() {
    int a = 5, b = 0;
    int result = (a > 0) || (b > 0);  // 结果为 1(真)
    printf("result: %d\n", result);  

    return 0;
}

条件运算符(三元运算符)

根据条件表达式的结果选择两个表达式中的一个,condition? expression1 : expression2,如果 condition 为真,结果为 expression1,否则为 expression2

#include <stdio.h>

int main() {
    int a = 5, b = 3;
    int max = (a > b)? a : b;  // 结果为 5
    printf("max: %d\n", max);  

    return 0;
}

赋值运算符:=+=-=*=/=%=<<=>>=&=^=|=

将右侧表达式的值赋给左侧的变量。赋值表达式的值就是被赋的值。例如,int b = (a = 5); 中,先将 5 赋给 a,然后将 a 的值赋给 b

#include <stdio.h>

int main() {
    int a;
    a = 10;  // 将 10 赋值给 a
    printf("a: %d\n", a);  

    return 0;
}

复合赋值运算符

结合了算术、移位或位运算与赋值操作,提高代码的简洁性,先进行算术、移位或位运算,然后将结果赋给左侧变量。

#include <stdio.h>

int main() {
    int a = 5;
    a += 3;  // 等价于 a = a + 3,结果为 8
    a -= 2;  // 等价于 a = a - 2,结果为 6
    a *= 4;  // 等价于 a = a * 4,结果为 24
    a /= 3;  // 等价于 a = a / 3,结果为 8
    a %= 5;  // 等价于 a = a % 5,结果为 3
    a <<= 1;  // 等价于 a = a << 1,结果为 6(二进制:110)
    a >>= 1;  // 等价于 a = a >> 1,结果为 3(二进制:011)
    a &= 2;  // 等价于 a = a & 2,结果为 2(二进制:010)
    a ^= 1;  // 等价于 a = a ^ 1,结果为 3(二进制:011)
    a |= 4;  // 等价于 a = a | 4,结果为 7(二进制:111)
    printf("a: %d\n", a);  

    return 0;
}

逗号运算符 ,

从左到右依次计算操作数,并返回最后一个操作数的值。

#include <stdio.h>

int main() {
    int a = (1, 2, 3);  // 结果为 3
    int b = 5, c = 10;
    int d = (b++, c++, b + c);  // 先执行 b++ 和 c++,然后计算 b + c,结果为 17
    printf("a: %d, d: %d\n", a, d);  

    return 0;
}

常用于在 for 循环的初始化或更新部分中执行多个操作

#include <stdio.h>

int main() {
    for (int i = 0, j = 10; i < 5; i++, j--) {
        printf("i: %d, j: %d\n", i, j);
    }

    return 0;
}
运算符优先级和结合性
  1. 优先级
    1. 运算符的优先级决定了表达式中运算的顺序,在复杂表达式中可能导致混淆。例如,在 a + b * c 中,先计算乘法 b * c,再计算加法。为避免混淆,可使用括号明确运算顺序,如 (a + b) * c
  2. 结合性
    1. 从左到右结合性:大多数二元运算符是从左到右结合的,如 a + b + c 先计算 a + b,再加上 c
    2. 从右到左结合性:一元运算符和赋值运算符通常是从右到左结合的,如 a = b = c 先将 c 赋给 b,再将 b 的值赋给 a

http://www.kler.cn/a/507978.html

相关文章:

  • 【从0带做】基于Springboot3+Vue3的心理健康预约平台
  • 人工智能-机器学习之多分类分析(项目实战二-鸢尾花的多分类分析)
  • DHCP详解和部署
  • LeetCode 热题 100 | 矩阵
  • springMVC实现文件上传
  • Ubuntu 安装和配置 MariaDB
  • 解答二重积分
  • 智能化量化模型的前沿探索:中阳的崛起
  • [BrainShadow-V1] VR头戴设备统计报告
  • 分布式系统、微服务、SOA初步理解
  • ubuntu设置代理服务器
  • “深入浅出”系列之设计模式篇:(0)什么是设计模式
  • Objective-C语言的数据类型
  • windows蓝牙驱动开发-BLE音频(一)
  • Linux测试处理fps为30、1920*1080、一分钟的视频性能
  • BGP边界网关协议(Border Gateway Protocol)概念、邻居建立
  • 【西藏乡镇界面】图层arcgis格式shp数据有乡镇名称和编码2020年wgs84坐标内容测评
  • C++/C语言判断重复数组(zznu)⭐
  • Java算法 二叉树入门 力扣简单题相同的树 翻转二叉树 判断对称二叉树 递归求二叉树的层数
  • 刷题记录 回溯算法-16:47. 全排列 II
  • 从玩具到工业控制--51单片机的跨界传奇【3】
  • NLP入门书籍《掌握NLP:从基础到大语言模型》免费下载pdf
  • MySQL的日期时间类型
  • 《汽车维修技师》是什么级别的期刊?是正规期刊吗?能评职称吗?
  • Vue.js组件开发-实现后端返回二进制文件在浏览器自动下载
  • 基于R语言的现代贝叶斯统计学方法(贝叶斯参数估计、贝叶斯回归、贝叶斯计算实践过程