news 2026/10/17 5:44:56

运算符与表达式练习:从陷阱到综合刷题指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
运算符与表达式练习:从陷阱到综合刷题指南

写运算符和表达式的练习,这事听起来特别基础,但说句实话,我带过不少初学者,也看过很多工作两三年的开发者在代码评审里翻车,问题往往就出在这块。不管是C、C++、Java还是Python,运算符和表达式永远是语言的第一道门槛,而且是一道“看着会、一写就错”的门槛。你觉得自己懂了+ - * /,实际上连个自增运算符都能倒下一片人。这篇文章我就围绕“运算符与表达式题目综合练习”这个主题,把我实际刷题、出题、带人复盘过程中积攒的经验全部摊开来讲,从原理到易错点,再到一套可以直接拿来练手的题目和解析,一次说透。

1. 这类题目到底在练什么,为什么值得花大力气

1.1 几乎所有程序Bug的源头都藏在这里

很多人写代码出问题,第一反应是查逻辑、查框架、查网络,绕了一大圈,最后发现是表达式求值顺序跟想象的不一样。比如int x = 5; int y = x++ + ++x;这种代码,在不同语言里结果可能不一样,甚至在同一门语言的不同版本里都可能存在未定义行为。很多公司面试题特别喜欢出这种题,不是为了炫技,而是因为运算符和表达式直接决定了你对语言底层机制的理解程度。

从实际项目来看,运算符相关的Bug主要集中在几个场景:

  • 复合赋值运算符用反了,比如total *= price + tax到底先加还是先乘
  • 位运算误用在逻辑判断上,把&和&&搞混
  • 浮点数直接用==比较
  • 短路求值带来的副作用忽略,比如if (a != null && a.length() > 5)里顺序写反导致空指针
  • 三目运算符嵌套过深,代码审查时没人看得懂

所以练运算符与表达式,本质上是在训练两种能力:第一是对语言规则的精确记忆,第二是对代码执行过程的脑内模拟能力。

1.2 这套练习适合谁,解决什么问题

如果你是刚学编程的学生,这套题能帮你把语法基础打牢,避免后续学数组、函数、指针时被表达式的各种隐含细节反复绊倒。如果你已经在工作中写业务代码,这套题能帮你快速定位那些藏在条件判断和计算逻辑里的隐蔽Bug,跟同事对代码时也能更有底气。如果你正准备面试或者刷算法题,那更是绕不开,很多算法题的第一步就是正确写出表达式,边界条件处理就是运算符基本功的直接体现。

我以前带过一个某高校的实习生A同学,写排序算法时比较器返回值理解和运算顺序搞错,导致排序结果时好时坏,排查了快一天。后来我把运算符和表达式的常见陷阱按类型整理成练习给他做,一周之后他写代码的老毛病明显减少。这种东西不是看一遍文档就能会,必须靠题量堆出敏感度。

2. 算数运算符:最基础但最阴险的五个陷阱

2.1 整数除法不是“除”,是“整除”

看这段代码:

int a = 7 / 2; // a = 3 double b = 7 / 2; // b = 3.0,不是3.5 double c = 7.0 / 2; // c = 3.5

很多初学者摔在第一行,甚至摔在第二行。第二行是重灾区:右边两个整数相除,结果先按整数除法算成3,再转换成double赋给b,所以拿到的不是3.5而是3.0。正确的写法是7 / 2.0或者(double)7 / 2。这点在C/C++和Java里都一样,Python3好一点,直接出浮点数,但Python2又不一样,所以日常写代码前先确认语言版本没错。

我自己刷题时习惯把“整型变量参与除法”看成两个步骤:先做整数除法,再做类型转换。这个思路能避免九成以上的类型转换错误。

2.2 取模运算的符号问题,比你想的复杂

取模运算%在正整数下很简单,一旦涉及负数就容易出错。C和C++里,结果符号跟被除数一致:

int a = -7 % 3; // a = -1 int b = 7 % -3; // b = 1

Python则不同,结果符号跟除数一致:

a = -7 % 3 # a = 2 b = 7 % -3 # b = -2

如果你写代码时没有意识到这个差异,做哈希、分页、循环队列这类场景时,索引可能直接越界或者出现诡异行为。我建议做题的时候先问自己三个问题:被除数是正是负?除数是正是负?语言规定的结果符号规则是什么?

2.3 自增自减:全局最常见翻车点

i++和++i的区别,字面上是“先使用后自增”和“先自增后使用”,但真正做题时经常被复合表达式绕晕。我给你们出个经典题,猜输出:

int i = 1; int x = i++ + ++i;

问x是多少?实话告诉你,在C和C++里,这个表达式的结果是未定义行为,编译器可能给出任何结果。因为同一个表达式里对i进行了多次有副作用的修改,并且没有序列点隔开。这种题拿去做,只会误导初学者。真正规范的教学题应该把副作用拆分开:

int i = 1; int x = i++; // x=1, i=2 int y = ++i; // i=3, y=3

所以做题要会分辨哪些题是考察规则,哪些题本身就是垃圾题。有个简单的判断标准:一个变量在同一个表达式中被修改了两次以上,且没有隐含的顺序约束,这种题可以直接跳过,别浪费时间去猜。

2.4 溢出与类型提升,边界值踩死人

比如Java里这个常见题:

int x = Integer.MAX_VALUE; x = x + 1; // 变负数,溢出

再比如混合运算的类型提升:

long result = 100000 * 100000; // 先按int运算,溢出后再提升为long,结果是负数 long correct = 100000L * 100000; // 正确写法

我在做题时的习惯是:混合类型运算,先判断最大操作数的类型,再看结果能否准确放下。特别是涉及字面量时,别嫌麻烦,该加后缀就加后缀。1e9这种double字面量参与取整运算时也要注意。

2.5 浮点数比较,永远别用等号

double a = 0.1 + 0.2; if (a == 0.3) { ... } // 大概率不成立

这是老生常谈,但每次练习还是要放进来。正确的比较方式是设定一个极小误差阈值:

double eps = 1e-9; if (fabs(a - 0.3) < eps) { ... }

做题时涉及到浮点数相等判断,第一反应就该想到精度问题。有些练习题故意用a = 0.3; b = 0.1 + 0.2;让你判断是否相等,这就是在考察浮点存储原理。

3. 逻辑运算符与短路求值:隐藏的行为决定成败

3.1 短路求值到底是什么

逻辑与&&和逻辑或||有一个重要性质:如果左边的结果已经能决定整个表达式的结果,右边就不执行了。这就是短路求值。

int a = 5; boolean result = (a > 3) || (a++ > 0); // 左边为true,右边a++不执行,a仍然是5

这类题看起来简单,但一旦换成函数调用或者赋值表达式,就很容易写错:

if (list != null && list.size() > 0) { // 顺序反了就会空指针 } if (list.size() > 0 && list != null) { // 危险写法 }

第二行一旦list为空就抛异常。所以我们在做逻辑判断时,常量、判空条件必须写在左边,这是铁律。

3.2 逻辑运算的返回值陷阱

在C和C++里,&&和||返回的是整数1或0,而在Java里,&&只能用在boolean上,&还能用在整数上做位运算。有些题会故意混搭:

int x = 2; int y = 1; int z = x && y; // C语言中结果是1,不是2

Python则不一样,and和or返回的是参与运算的对象本身,而不是布尔值:

a = 0 and 2 # a = 0 b = 1 and 2 # b = 2 c = 0 or 3 # c = 3

这个特性常常被用来写“默认值”逻辑,比如name = input_name or "default",但前提是你清楚知道空字符串、0、None这些值会被当作假来处理,否则容易埋雷。

3.3 从题目里识别“副作用陷阱”

我在综合练习里专门设计了这样一道题:已知a=1, b=2, c=3,求(a++ > 0) && (b++ > 0) && (c++ > 0)之后 a、b、c 各自的值。答案不是简单的都加了1,而是要从左到右一步步模拟:先算a++ > 0,a变成2,结果是true;接着算b++ > 0,b变成3,true;接着算c++ > 0,c变成4,true。如果第一项是false,后面两个都不会执行。

这种题的做题方法就是一个词:逐步展开。把每个带副作用的子表达式拆开,标注前后值变化,千万别用“直觉”一口气判断。

4. 位运算:笔试和面试的最爱,也是实用技能

4.1 基础位运算要形成肌肉记忆

位运算总共就那么几种:左移<<、右移>>、无符号右移>>>(仅Java等)、按位与&、按位或|、按位异或^、取反~。

做题时必考的几个点:

  • 判断奇偶:x & 1 == 1比x % 2 == 1更高效
  • 交换两个数不用临时变量:a ^= b; b ^= a; a ^= b;
  • 清除最低位的1:x & (x - 1)
  • 计算二进制中1的个数(Brian Kernighan算法源起处)
  • 判断是不是2的幂:x > 0 && (x & (x - 1)) == 0

比如 2 的幂判断,原理在于2的幂二进制只有一个1,减1之后借位让后面的位全变1,与原数相与必然为0。学了原理之后就不需要背结论。

4.2 右移的高频坑:算术右移还是逻辑右移

C/C++里,对有符号数>>是算术右移,最左边补符号位;对无符号数是逻辑右移,最左边补0。负数右移不等于除以2的向下取整,有些题会拿这个做文章。

int x = -8; int y = x >> 1; // -4,常见实现是算术右移

Java里分两种:

int x = -8; int a = x >> 1; // -4,算术右移 int b = x >>> 1; // 2147483644,逻辑右移,最左边补0

做题时看到>>>和>>混在一起,第一反应就是检查符号位处理。这个点我在实际面试中见过很多次,经常出现在“不用乘除法实现除以2”的问题里直接把人送走。

4.3 位运算综合题:状态压缩初体验

有一类经典题:用整型变量的若干位来表示多个布尔开关,比如一个字节的高4位表示4种权限,低4位表示另外4种状态。然后要求写代码去设置、清除、反转某一位。

// 按位设置第n位 x |= (1 << n); // 清除第n位 x &= ~(1 << n); // 取反第n位 x ^= (1 << n); // 读取第n位 (x >> n) & 1

这类题考的是位掩码构造和“先清零再置位”的思路,非常贴近底层开发的日常。写业务代码的人可能接触不多,但做嵌入式、网络协议、高性能计算一定会用到。

4.4 位运算的优先级是所有人的公认噩梦

按位运算符&、^、|的优先级都低于关系运算符,比如x & 1 == 0会先算1 == 0,结果完全不是你想的那样。正确的写法是(x & 1) == 0。

C语言里这段代码是个经典反面教材:

int x = 5; if (x & 1 == 0) { // 等价于 x & (1==0),即 x & 0,恒为0,条件恒为假 }

正确写法是if ((x & 1) == 0)。我在综合练习题里面专门放一道这种题,想让读者建立条件反射:位运算两侧一定要加括号。

5. 赋值运算符与复合赋值运算符:你以为你懂,其实你不懂

5.1 复合赋值自带强转属性

Java中x += 1并不完全等价于x = x + 1,区别在于复合赋值会自动进行一次窄化转换:

short s = 1; s = s + 1; // 编译错误:s+1是int,不能赋给short s += 1; // 编译通过,等效于 s = (short)(s + 1)

这题很多人第一次看到都表示震惊。C语言类似但细节不同,所以做题必须先锁定语言。我的建议是:把所有复合赋值运算符的“类型转换行为”单独整理一遍,这比死记运算符优先级划算得多。

5.2 连等运算要注意结合方向

Java里a = b = c = 3;从右往左结合,最终a、b、c都是3。但C语言里连等不是总有合法类型,例如a = b = c = 3;虽然从右往左赋值,但链式赋值要求两侧类型匹配。实际项目里我几乎不写连等赋值,可读性太差,Debug时又没法在中间打断点观察。练习题里考这个,主要是让你理解赋值运算符是右结合这一点。

5.3 复合赋值中表达式只求值一次

有一个隐藏细节:a[i++] += 2等价于a[i++] = a[i++] + 2吗?答案是否定的。复合赋值中左边的变量或数组引用只求值一次,所以a[i++] += 2实际上只是先取出i计算位置,再在该位置上加2,i自增发生在哪一步需要看语言规范。

C标准里这个行为在某些场景下也涉及到未定义问题,像a[i++] += 2这种,C++里既有修改副作用又有读取,非常危险。我刷题看到这类代码,第一反应就是跑一遍看看不同编译器的结果,不建议死记答案。真正规范的教学练习里应该写成:

int idx = i++; a[idx] += 2;

这样语义清晰、所有编译器结果一致。

6. 运算符优先级:从根源上理解,而不是背表格

6.1 一张表搞定95%的题目

优先级的表格网上到处都是,背下来难度其实不大,真正难的是在复合表达式里灵活应用。我的建议是记几组关键分层:

第一层:括号、数组下标、函数调用、成员访问 第二层:单目运算符(! ~ ++ -- 正负号) 第三层:算术运算符(* / %高于+ -) 第四层:移位运算符(<< >> >>>) 第五层:关系运算符(< > <= >=) 第六层:相等运算符(== !=) 第七层:位运算符(&高于^高于|) 第八层:逻辑运算符(&&高于||) 第九层:三目运算符 第十层:赋值运算符

有了这个分层,做题时先在脑内给表达式套括号,再逐步求值。

6.2 两个典型的高频坑

先来看第一个:

int x = 2; if (x = 3) { // 赋值而不是比较,条件恒为true }

很多语言里赋值表达式的结果就是被赋的值,非零即为真。这种题在C/C++的老题里频繁出现,考察的是你对“赋值也是表达式”的理解。严谨的代码应该写成if (3 == x),避免手滑少写一个等号。

再看第二个:

int a = 2, b = 3; int result = a > b ? a : b + 1; // 先算b+1,再取两者较大值?错了,条件运算符优先级很低

实际上这个表达式的解析是a > b ? a : (b + 1),因为条件运算符优先级低于关系运算符。做题时如果没有形成“低优先级运算符让整个表达式分段”的意识,很容易按从左到右的顺序晕掉。我的做题习惯是:找出表达式里优先级最低的运算符,先从那里切一刀,把原式拆成若干个子表达式,再逐步求值。

6.3 括号策略:代码是写给人看的,然后是机器

虽然规则背熟了可以不用括号,但是我在实际项目里强烈建议:只要混合用了两种以上运算符,就加括号。比如a & b == 0,就算你知道它的解析是a & (b == 0),阅读你代码的人不一定知道。练习题里那些“不加括号算结果”的题只是训练,真实工程里代码可读性优先。

7. 综合练习题库:直接上手刷起来

7.1 基础入门的10道题(先说结论再讲思路)

我设计了以下题组,覆盖算数、逻辑、位运算和优先级:

// 第一组:算数基础 1. int a = 7 / 2; a 的值? 2. int b = 7.0 / 2; b 的值? 3. int c = -7 % 3; c 的值? (C语言) 4. double d = 0.1 + 0.2; d == 0.3 是否成立? 5. int i = 2; i += i++ + ++i; i 是?

第一题答案是3,第二题如果直接赋给int会编译报错,Java里直接编译不过,C里会有警告但结果还是3。第三题C里是-1,Python里是2。第四题不成立,浮点精度问题。第五题是典型垃圾题,存在未定义行为,不给出答案才是正确的答案。做题时碰到这种第五题,要能识别出来,比计算出某个具体值更重要。

7.2 中级挑战的10道题(结合真项目需求)

进入中级,我更倾向于包装成“小情景”,而不是干巴巴的算术题。比如这道题就来自一个分页功能:

int page = 0; int totalPages = ...; boolean hasNext = page++ < totalPages; // 问题是:page和hasNext分别变成多少?

答案是:先比较page(0)是否小于totalPages,再把page变成1,hasNext取决于比较结果。这里若把page++ < totalPages改成++page < totalPages,行为就完全不同了。这类题在实际业务代码中出现频率奇高,你能在几秒内说出两个写法的区别,基本就能秒杀绝大多数面试场景。

再放一道经典的位运算升级题:

int x = 0b101100; int count = 0; while (x != 0) { x = x & (x - 1); count++; } // count等于几?

先把0b101100转成十进制是44,二进制含3个1,循环每次清除最右边的1,所以count是3。这类题考察的就是x & (x - 1)到底干了什么,属于刷题高频考点。

7.3 进阶项目的10道题(一题多解才有意思)

进阶题我不追求偏难怪,而是追求一题多解和边界覆盖。比如这道:不用条件语句求x的绝对值:

int abs = (x ^ (x >> 31)) - (x >> 31);

把x >> 31当成一个掩码,若x为正则右移31位得0,若为负则得-1(全1),通过异或和减法算出绝对值。能理解这行代码的人,对算术右移、异或、补码的理解就到位了。

再给一个经典题:判断某年是否是闰年,要求只能用一条表达式:

boolean leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;

这题不是运算符技巧本身有多难,而是考察你把数学条件和逻辑表达式结合的能力,顺序、括号都不能错。初学者常常少写一个括号导致算错,题目设计得很值得练。

7.4 每道题的评分标准建议

做练习时建议给自己打分:

  • 能一次写对,并且能解释为什么这样算,满分
  • 能写对但说不出理由,算半对,说明还要回头啃基础
  • 写错了,别急着看答案,先用断点调试或者手写步骤推导,再看解析

我强烈建议把“写出解题过程”作为训练核心,而不是只看最终结果。你每推导一次,就是在模拟一次CPU执行流程,这对后续学指针、递归、并发都有巨大帮助。

8. 刷题的高效方法:我踩过坑也总结出规律

8.1 分类整理远比盲目刷题重要

我见过不少新人上来就啃几百道“运算符题”,结果题型一变就懵。更合理的做法是按本节前面拆的类别——算数陷阱、短路求值、位运算、优先级——每种类型集中刷10到20道,直到一个类型做到90%以上正确率再换下一个。这样每类题型都形成了独立的知识组块。

我自己刷题时,每做完一道题会顺手贴到自己的错题本里,分成两类:一类是“规则不知道”,这类错题说明知识点有漏洞,需要回到文档里补背景;一类是“步骤跳步”,这类错题说明习惯不好,做题太依赖直觉。两类错误的复习频率不太一样,前者需要每周回顾,后者每天提醒自己放慢速度就好。

8.2 用“口述复现法”检验掌握程度

关于运算符表达式题,有一个我屡试不爽的检验方法:把题目遮住,向身边人或者镜子口述这道题的完整推演过程,包括每一步的运算符结合方向和值的变化。如果你能流畅讲出来,说明真正掌握了;如果讲到一半去抓笔算,说明还没形成内化记忆。

我当年在组织某模拟项目X的代码评审时,要求参与者轮流讲解自己写的表达式为什么这样写,结果半个小时的会议活活开成了两个小时。过程中暴露了大量“能跑但说不清”的代码,这在团队协作里是隐患,因为后续没人敢重构它。

8.3 实战项目里怎么继续保持敏感度

刷阶段题只是建立基础,真正让运算符与表达式的感觉上升一层,还得靠写实际项目。我建议在日常开发中刻意做三件事:

  • 每个条件表达式的运算符两侧强制加括号,先不用管“多余不多余”
  • 每次用完取模、位运算和复合赋值,在代码旁边写一行注释,说明这里在干什么数学变换
  • 定期回看一个礼拜前自己写的表达式代码,能一眼看懂就说明可读性合格,需要仔细想才看懂说明当初的表达方式就有问题

这样持续一个月,你对运算符的敏感度会明显超过很多人。很多资深开发者所谓的“代码直觉”,其实就是大量规则内化之后的表达习惯。

最后再分享一个小技巧:做运算符与表达式练习时,建议自己在本地写一个很小的测试工程,把每道题用printf输出结果验证,然后用眼睛对着输出看一遍,再跟推导过程对照。有时候推导和实际输出不一致,不是规则记错了,而是没考虑编译器的具体实现,这种时候也要值得高兴,因为你正好抓到了语言规范和编译器实现之间的灰色地带。这个灰色地带,恰恰才是区分你和普通开发者的分水岭。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 5:44:14

C语言结构体详解:数组装不下时如何自定义类型

不知道你注意到没有&#xff0c;学 C 语言到数组这一章&#xff0c;一切还算顺风顺水&#xff1a;一维数组、二维数组&#xff0c;无非是往箱子里塞数据。可一旦碰到"学生信息管理"这类题目&#xff0c;很多人就懵了——一个人身上有姓名、学号、成绩、年龄、性别&am…

作者头像 李华
网站建设 2026/10/17 5:44:12

卷积定理详解:从时域卷积到频域乘积及调制加窗应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 5:43:48

马尔可夫转移矩阵全解析:从数据构造到稳态分布与实战应用

做数据分析这行&#xff0c;多多少少都会碰到一类问题&#xff1a;手里有一串状态序列&#xff0c;想预测下一步最可能是什么&#xff0c;或者想给一堆节点按“重要程度”排个序。搜索引擎的网页排名、App 里的个性化推荐、甚至天气预报里“今天晴明天雨”的概率&#xff0c;背…

作者头像 李华
网站建设 2026/10/17 5:42:11

【c++】STL-容器list的使用

hello~ 很高兴见到大家! 这次带来的是C中关于list容器这部分的一些知识点,如果对你有所帮助的话,可否留下你的三连呢? 个 人 主 页: 默|笙 文章目录一、list的构造二、迭代器分类三、Operations成员函数3.1 sort&#xff08;了解&#xff09;高速缓存命中率3.2 其他的一些函数…

作者头像 李华
网站建设 2026/10/17 5:42:00

GitHub_Trending/templa/templates模板使用统计:最受欢迎的10个模板分析

GitHub_Trending/templa/templates模板使用统计&#xff1a;最受欢迎的10个模板分析 GitHub_Trending/templa/templates是基于开源新版QD框架站发布的公共har模板库&#xff0c;提供了丰富的网站自动化操作模板&#xff0c;帮助用户轻松实现各类网站的签到、数据获取等功能。下…

作者头像 李华
网站建设 2026/10/17 5:40:52

Webpack核心原理与企业级构建实战指南

1. 项目概述&#xff1a;为什么Webpack不是“配菜”&#xff0c;而是前端工程的主厨&#xff1f;“【谷粒学院】013-Webpack”这个标题乍看像一门课程编号&#xff0c;但背后藏着一个真实到骨子里的行业切口——它不是教你怎么写console.log(Hello)&#xff0c;而是在教你怎么把…

作者头像 李华