下面两个我遇到麻烦的问题来自K.N. King的“C Programming a Modern Approach”中的第5章练习3。
1) i = 7; j = 8; k = 9;
printf("%d ",(i = j) || (j = k));
printf("%d %d %d\n", i, j, k);
// answer I think it is 1 9 9 9
// actual answer after running in C: 1 8 8 9
2) i = 1; j = 1; k = 1;
printf("%d ", ++i || ++j && ++k);
printf("%d %d %d\n", i, j, k);
// answer I think it is 1 2 2 2
// actual answer after running in C: 1 2 1 1
我的问题如下:
1.为什么问题1中的i和j的值在第一个printf语句中赋值后没有改变?因为赋值运算符表达式(i = j)和(j = k)在括号中,它们从右到左首先被求值。此时它们的值应该是9和9。我很困惑为什么第二个printf语句输出8和8的i和j?
1.问题2中的j和k的值在第一个printf语句中递增后为什么没有改变?由于前缀递增具有更高的优先级,因此它们首先从右到左计算i,j和k。此时它们的值应该是2,2和2。像问题1一样,为什么第二个printf语句输出1和1 j和k?
4条答案
按热度按时间lskq00tm1#
当你有
expr1
将首先被评估。如果
expr1
的结果为真(也称为非零),则expr2
永远不会被计算,expr1 || expr2
的结果将为1。如果
expr1
的结果为假(也称为零),则将计算expr2
。如果expr2
的结果为非零,则expr1 || expr2
的结果将为1。否则,它将为零。所以这行:
可以重写为:
对
printf("%d ", ++i || ++j && ++k);
使用同样的想法,您将看到为什么只有i
发生了变化。pinkon5k2#
首先,运算符优先级 * 仅 * 控制运算符与操作数的分组,它 * 不 * 影响求值顺序。
其次,请记住,
||
和&&
运算符都是从左到右计算和短路的。首先计算左操作数,并应用任何副作用。根据结果(&&
为0
,||
为非零)右操作数根本不会被计算 *,并且不会应用右手操作数中的任何副作用。在声明中
首先计算表达式
(i = j)
,因此i
现在具有j
的值,即8
。由于结果为非零,因此根本不计算表达式(j = k)
;表达式的结果是1,并且j
不被更新(它保持为8
)。在声明中
首先计算表达式
++i
(因此i
现在的值为2
);因为结果是非零的,所以++j && ++k
根本不被计算(运算符优先意味着表达式被解析为++i || (++j && ++k)
),并且j
和k
都不被更新,所以它们都保持为1
。bwleehnv3#
让我们考虑第一个代码片段
在
printf
的第一次调用中使用具有逻辑OR运算符的表达式。
来自C标准(6.5.14逻辑或运算符)
3The||如果运算符的任一个操作数比较不等于0,则运算符将产生1;否则,结果为0。结果的类型为int。
4不像按位|操作员||操作符保证从左到右求值;如果第二个操作数被求值,则在第一个操作数和第二个操作数的求值之间存在一个序列点。如果第一个操作数比较不等于0,则不对第二个操作数求值。
由于表达式的左操作数
(i = j)
不等于0
,因此不计算右操作数(j = k)
。因此,在赋值后
(i = j)
i
等于j
,即等于8
。根据C标准引用的表达式的结果等于1
。因此,第一次调用
printf
时,输出的逻辑OR运算符表达式的结果等于1
,下一次调用printf
时,输出的变量i
、j
、k
等于8
、8
、9
。现在让我们考虑第二个代码片段
在
printf
的第一次调用中,还使用了一个带有逻辑OR和逻辑AND运算符的表达式。同样,由于逻辑OR运算符的操作数
++i
不等于0
(它的值在递增i
之后等于2
),则逻辑OR运算符( ++j && ++k )
的右操作数不被求值,并且printf
的调用相应地输出1
、2
、1
、1
.如果要按以下方式交换逻辑OR运算符的操作数,那么考虑第一个调用或
printf
是很有趣的使用的表达式等效于
具有逻辑OR运算符的表达式的第一个操作数是具有逻辑AND运算符的表达式。
根据C标准(6.5.13逻辑与运算符)
3**如果&&运算符的两个操作数比较都不等于0,则&&运算符将产生1;**否则,结果为0。结果的类型为int。
4与按位二元&运算符不同,&&运算符保证从左到右求值;如果第二个操作数被求值,则在第一个操作数和第二个操作数的求值之间存在序列点。如果第一个操作数比较等于0,则第二个操作数不被求值。
在这种情况下,由于逻辑AND运算符的第一操作数
++j
不等于0
(在递增j
后表达式的值等于2
),则第二个操作数++k
也将被计算,并且其值也不等于0
(在递增k
之后,它等于2
)。因此,由于此时逻辑OR运算符的左操作数( ++j && ++k )
不等于0
,则右操作数++i
将不被求值,结果输出将是1, 1, 2, 2
。另外,请考虑以下代码片段
第一次调用
printf
将输出1
,而第二次调用printf
将输出2
。然后呢
在这种情况下,第一次调用
printf
也将输出1
(逻辑OR和逻辑AND运算符的结果是1
或0
,根据C标准提供的报价)。第二个调用或printf
将输出4
,因为将计算具有逻辑AND运算符的表达式的所有操作数。nhhxz33t4#
printf("%d ",(i = j) || (j = k));
是混淆的垃圾,将替换为:printf("%d ", ++i || ++j && ++k);
是混淆的垃圾,将替换为:这本书的作者想让你学到什么:
我们实际学到的:
调试的难度是编写代码的两倍,因此,如果你尽可能聪明地编写代码,那么你就没有聪明到可以调试它。
++i || (++j && ++k)
,因为每个人可能都不清楚&&
的优先级高于||
。但这并不重要。在这个练习之后我们必须忘掉危险的废话:
++
操作符与其他操作符混合使用是危险的,容易出错,应该避免。这种脆弱的垃圾代码能够按预期工作的唯一原因是因为||
和&&
具有定义良好的从左到右求值,并且操作数之间有一个 * 序列点 *。这通常不是C操作符的情况。例如,
(i = j) | (j = k)
(按位OR而不是逻辑)是未定义的行为,是完全破坏的代码。Why are these constructs using pre and post-increment undefined behavior?
或者以更紧凑的形式:Why can't we mix increment operators like i++ with other operators?