信息学奥赛(CSP-J)零基础启蒙培训讲义
第4天:算术与逻辑的交织(运算符与表达式)
- 总时长:6 小时(上午 3 小时,下午 3 小时)
- 培训目标:通过生动的生活化比喻,帮助零基础学生彻底掌握 C++ 中的算术运算、类型转换、关系运算与逻辑表达式,避开初赛选择题中经典的“整数除法截断”等巨坑。
📅 上午场:算术运算符与类型转换(09:00 - 12:00)
一、 算术运算符与“整数除法陷阱”
1. 基本算术运算符
在 C++ 中,加减乘除和数学里大同小异:
* + (加法)
* - (减法)
* * (乘法)
* / (除法)
* % (取模/求余数,注意:操作数必须都是整数)
2. ⚠️ 巨坑警示:整数除法截断(初赛第一错题点)
- 规则:在 C++ 中,如果除号两边全都是整数,那么结果也必须是整数。
- 后果:它会直接无条件舍弃小数部分(向零取整),绝不进行四舍五入!
- 例子:
5 / 2的结果是多少?不是 2.5,而是2!(小数点后面直接被砍掉)。- 如果你想得到 2.5,两边必须至少有一个浮点数(小数),写成
5.0 / 2或者5 / 2.0,结果才是2.5。 - 经典初赛真题陷阱:计算
s = 1 + 1/2 + 1/3 + ... + 1/10。 - 如果写成
s = s + 1 / n;,因为1和n都是整数,1 / n的结果永远是0(当 $n > 1$ 时),导致整个求和结果全变成了 1.0!必须写成1.0 / n。
二、 隐式类型转换与自增自减
1. 隐式类型转换
当不同类型的数据放在一起做运算时,计算机为了不丢失精度,会自动把“小类型”提升为“大类型”。
* 比如 int(整数)和 float(小数)相加,整数会先自动变成小数,再相加,最后结果是小数。
2. 自增(++)与自减(--)
程序员为了偷懒,发明了让变量自己给自己加 1 的简写符号:
* i++(后置自增):先用后加。先把当前的 i 拿去参与表达式计算,用完了之后,自己的值再加 1。
* ++i(前置自增):先加后用。自己先加 1 变成新值,再把新值拿去参与表达式计算。
* 例子:
cpp
int a = 5;
int b = a++; // b 的值是 5,随后 a 变成了 6
int c = 5;
int d = ++c; // c 先变成 6,随后 d 的值是 6
---
📅 下午场:关系运算符与逻辑表达式(14:00 - 17:00)
一、 关系运算符(比大小)
我们要让计算机帮我们做判断,首先得会比较大小。C++ 提供了 6 种关系运算符:
* > (大于)
* < (小于)
* >= (大于等于)
* <= (小于等于)
* == (等于 —— 注意:千万别写成单个 =,单个 = 是赋值!)
* != (不等于)
💡 核心规则:关系运算的结果只有两种——真(true,在计算机里表现为数字 1)或者假(false,在计算机里表现为数字 0)。
二、 逻辑运算符(把条件串联起来)
现实中我们的判断往往有多个条件。比如:“如果成绩大于等于 90 分,并且(&&)操育分也是优,就能评三好学生”。
C++ 提供了三大逻辑运算符:
1. 逻辑与(&&,相当于生活中的“并且”)
- 规则:左右两边的条件必须同时为真(true),整个大条件才为真;只要有一边是假(false),整体就是假。
- 生活比喻:妈妈说:“期末数学要考 100 分,并且英语也要考 100 分,才给你买手机。” 结果数学 100,英语 99 —— 手机泡汤。
2. 逻辑或(||,相当于生活中的“或者”)
- 规则:左右两边的条件只要有一边为真(true),整个大条件就为真;必须两边同时为假,整体才是假。
- 生活比喻:爸爸说:“今天要么你去洗碗,或者我去扫地,这事就算完了。” 只要一个人干了,任务达成。
3. 逻辑非(!,相当于生活中的“取反 / 唱反调”)
- 规则:单目运算符。把真的变成假的,把假的变成真的。
- 例子:
!(5 > 3)。因为5 > 3本来是真的(1),取反之后就变成了假(0)。
三、 逻辑表达式的“短路求值”原则
在 C++ 计算复杂的逻辑表达式时,有一个非常聪明的“偷懒”机制叫短路求值(Short-circuit Evaluation):
1. 对于 &&(与):如果左边已经是假(false)了,因为“一假俱假”,计算机直接懒得看右边是什么了,当场判定整个表达式为假。
2. 对于 ||(或):如果左边已经是真(true)了,因为“一真俱真”,计算机同样懒得看右边,直接判定整个表达式为真。
* 注意:这种机制有时会影响右边表达式里的自增(如 ++)是否会被执行,这也是初赛代码阅读题喜欢挖坑的地方。
---
📝 随堂与课后实战强化练习卷(学生版)
班级:__ 姓名:__ 得分:__
一、 选择题(共 10 题)
-
在 C++ 中,表达式
7 / 3的计算结果是( )。 A. 2.3333 B. 2 C. 3 D. 2.0 -
设有定义
int a = 3, b = 4;,则表达式(float)a / b的计算结果是( )。 A. 0 B. 0.75 C. 1 D. 0.0 -
设
int x = 5; int y = x++;,执行完这两行代码后,变量x和y的值分别是( )。 A.x = 5, y = 5B.x = 6, y = 5C.x = 6, y = 6D.x = 5, y = 6 -
下列逻辑表达式中,其值为“真”的是( )。 A.
(5 > 3) && (2 > 4)B.(10 % 3 == 0) || (5 != 5)C.!(4 > 5)D.(3 == 4) && (1 || 0) -
设
bool a = true, b = false, c = true;,则逻辑表达式(a || b) && (!c || b)的值是( )。 A.true(真) B.false(假) C. 1 D. 无法确定 -
在 C++ 中,判断整型变量
x是否为偶数的正确表达式是( )。 A.x / 2 == 0B.x % 2 == 1C.x % 2 == 0D.x = 2 -
设有代码
int x = 0; int y = (x++) + (++x);,请问执行后y的值是(注:不同编译器底层实现细节可能略有差异,按标准自增结合律理解)?( ) A. 1 B. 2 C. 3 D. 0 -
表达式
13 & 1 == 1在 C++ 中的计算结果(注意运算符优先级,算术/位运算与关系运算)是( )。 A.true(真) B.false(假) C. 13 D. 1 -
下列关于短路求值的叙述中,错误的是( )。 A. 在表达式
A && B中,如果A为假,则不会去计算BB. 在表达式A || B中,如果A为真,则不会去计算BC. 短路求值可能会导致右侧表达式中的函数没有被调用或自增没有发生 D. 短路求值会导致整个逻辑表达式的最终计算结果发生错误 -
设有变量
double s = 1.0; int n = 10;,若想通过循环计算s = s + 1/n,但由于整数除法陷阱导致结果错误,为了让1/n能够正确进行浮点数除法,下列修改方法中错误的是( )。 A.s = s + 1.0 / n;B.s = s + (float)1 / n;C.s = s + 1 / (float)n;D.s = s + (float)(1 / n);
(注:教师答案与详细解析页在下方,建议打印前单独切分)
\newpage
📖 课后练习卷 —— 标准答案与详细解析
- 正确答案:B
-
详细解析:
7和3都是整型,7 / 3在 C++ 中进行整数除法,直接截断小数部分,结果为2。故选 B。
-
正确答案:B
-
详细解析:
(float)a把a临时强制转换为浮点数3.0。- 浮点数除以整数(
3.0 / 4),触发隐式类型提升,结果为浮点数0.75。故选 B。
-
正确答案:B
-
详细解析:
y = x++;是后置自增。先把原值5赋给y(所以y = 5),随后x自身加 1 变成6(所以x = 6)。故选 B。
-
正确答案:C
-
详细解析:
- 选项 A:
(5 > 3)为真,但(2 > 4)为假,&&结果为假。 - 选项 B:
10 % 3 == 1(假),5 != 5(假),||结果为假。 - 选项 C:
4 > 5为假,取反!之后变为真(true)。 - 选项 D:
3 == 4为假,&&整体直接短路为假。 - 因此,值为真的只有 C。
- 选项 A:
-
正确答案:B
-
详细解析:
- 代入布尔值:
a = true, b = false, c = true。 (a || b)$\rightarrow$(true || false)=true。(!c || b)$\rightarrow$(!true || false)=(false || false)=false。true && false=false(假)。故选 B。
- 代入布尔值:
-
正确答案:C
-
详细解析:
- 判断一个数是不是偶数,看它除以 2 的余数是否为 0,即
x % 2 == 0。选项 D 中的=是赋值符号,会导致错误。
- 判断一个数是不是偶数,看它除以 2 的余数是否为 0,即
-
正确答案:B
-
详细解析:
- 依现代编译器标准,该复合自增表达式存在顺序点或未定义行为隐患,但在经典严谨的初赛考题或单侧递增中,若考查基础:
x++值为 0 随后x变为 1,接着++x使x变为 2 并返回值 2,相加为 2。故选 B。(注:部分老旧教材或特定编译器结果可能不同,但在大纲中重点考查前、后置自增的交替影响)。
- 依现代编译器标准,该复合自增表达式存在顺序点或未定义行为隐患,但在经典严谨的初赛考题或单侧递增中,若考查基础:
-
正确答案:B
-
详细解析:
- 注意运算符优先级:算术/位运算的优先级高于关系运算符。
- 表达式
13 & 1 == 1中,==的优先级低于&。 - 因此它实际上先计算
1 & 1(结果为 1),然后再计算13 == 1(结果为false)。所以最终逻辑结果是false。
-
正确答案:D
-
详细解析:
- 短路求值是 C++ 语言标准的合法机制,它能提高效率并保证某些安全判断(例如先判断指针不为空
p != NULL && p->val == 1),它不会导致最终计算结果发生错误。选项 D 说法错误。
- 短路求值是 C++ 语言标准的合法机制,它能提高效率并保证某些安全判断(例如先判断指针不为空
-
正确答案:D
- 详细解析:
- 选项 D 中
(float)(1 / n)是先进行了整数除法1 / n(结果已经变成了0),然后再把这个0强制转换为0.0f,依然掉进了整数除法的陷阱。 - 选项 A、B、C 都能在除法发生前成功把
1或n变成浮点数,从而避免截断。故选 D。
—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com