csapp-homework-2.74
这里挂一下csapp的2.74问题和答案,进行分析,学到了条件判断然后赋值的新思路。
Write a function with the following prototype:
/* Determine whether arguments can be subtracted without overflow */ int tsub_ok(int x, int y); This function should return 1 if the computation x-y does not overflow
首先这个题目并没有要求使用164页的编程规则限制,也就是说你其实可以用条件判断和循环来实现这个题目。然后另一个问题就是,怎么样才能更简单实现这个功能。
我的思考肯定是基于这两个判断表达式的:
if(x < 0 && y > 0 && sub > 0) return 0; //负溢出 if(x > 0 && y < 0 && sub < 0) return 0; //正溢出
然后可以看看答案是怎么考虑这个问题的,之所以记录这个题目,就是因为这个题目运用了逻辑运算与&&
的短路特性来使得代码简化了(随之而来的是可读性变差的结果):
/* * tsub-ok.c */ #include <stdio.h> #include <assert.h> #include <limits.h> /* Determine whether arguments can be substracted without overflow */ int tsub_ok(int x, int y) { int res = 1; /* 如果y为INT_MIN,则x为任意值都会溢出: x > 0,sub < 0; x < 0(x此时取值范围为 [INT_MIN,0) ),sub >= 0--突然发现这个条件是判断是否溢出的一个定理,可以重点记忆一下 */ (y == INT_MIN) && (res = 0); // if (y == INT_MIN) res = 0; /*这里就是很正常的条件判断,但是利用了逻辑与 && 运算符的短路特性*/ int sub = x - y; int pos_over = x > 0 && y < 0 && sub < 0; int neg_over = x < 0 && y > 0 && sub > 0; res = res && !(pos_over || neg_over); return res; } int main(int argc, char* argv[]) { assert(!tsub_ok(0x00, INT_MIN)); assert(tsub_ok(0x00, 0x00)); return 0; }
可以看到这个题目中,用到了逻辑运算与 &&
运算符的短路特性来判断是否溢出了。用chatgpt来分析这个 (y == INT_MIN) && (res = 0);
语句实现了什么功能,可以得到答案:
这个表达式使用了逻辑与
&&
运算符,它具有短路特性。表达式中有两部分:
(y == INT_MIN)
:检查变量y
是否等于INT_MIN
,即判断y
是否为整型最小值。(res = 0)
:将变量res
赋值为0
。这两部分由
&&
连接在一起,整个表达式的结果取决于两部分的值。如果第一部分
(y == INT_MIN)
为真(即y
等于INT_MIN
),那么第二部分(res = 0)
也会被执行。这是因为在逻辑与运算中,如果第一个条件为真,那么第二个条件就会被检查并执行。在这种情况下,整个表达式的值为真。如果第一部分
(y == INT_MIN)
为假,那么第二部分(res = 0)
将不会被执行,因为短路特性会导致整个表达式的值为假。这样的表达式通常用于条件赋值的场景。如果
y
的值等于INT_MIN
,则将res
的值设置为0
;否则,保持res
的原始值。在实际编码中,这种写法可能会让代码可读性变差,应该慎用。最好将条件判断和赋值操作分开,以提高代码的清晰度。
那么其实这个题目用了某种取巧的方法简化了代码结构,其实可读性变差了。但是仍然加强了对于逻辑运算符的理解。
其实2.74还蕴藏着一个知识点:两个数的和什么时候会溢出。
当x + y > TMAX,或者x + y < TMIN时,这时候就会导致溢出。若根据这两个判断条件来确定当溢出时x和y的范围,则需要分类讨论:
- x > 0时,因为TMIN < y < TMAX,所以x + y > TMIN + x是永远不会负溢出的。但是可能会出现x+ y > TMAX的正溢出的场景。此时x > 0,那么y的取值范围就是(TMAX - x, TMAX]
- x < 0时,x + y = TMAX + x永远不会正溢出。但是会出现负溢出(x + y < TMIN)的场景。y的取值范围是[TMIN, TMIN - x)
然后回顾2.74题目,发现在最后返回是否溢出的时候,两个溢出标志用了逻辑运算符||
:
res = res && !(pos_over || neg_over);
在这分析下这句话的作用:
首先是括号内的pos_over || neg_over
,这里的意思是:当正溢出或者负溢出时候,此条件成立。即只要是溢出了,则此括号内的运算结果为1。
那此时!(pos_over || neg_over)
就相当于一个mask:若计算结果溢出,则返回0;若不溢出,则返回1。
那我有个疑问,这个mask就是一个根据是否正溢出或者负溢出而判断溢出的标志掩码,那么我能否在括号内将||
换为&&
?
那此时只要有一个溢出标志为假,那么这个mask的取值就会成为:若两边都溢出,则返回0;若其中有一个溢出或者都不溢出,则返回1。这样与题目的要求不符合:只要溢出,则返回0。
所以这个||
不能换为&&
。
(存货,写于2024年2月2号)
本文作者:上山砍大树
本文链接:https://www.cnblogs.com/shangshankandashu/articles/18023845
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步