从带有签名的原始代码开始
char f2( int a, unsigned b );
这包含表达式
a + b
由于这些操作数之一具有有符号整数类型,另一个具有(相应的)无符号整数类型(因此它们具有相同的“整数转换等级”),那么 - 遵循“常规算术转换”(第 6.3.1.8 节) - 操作数为有符号整数类型转换为另一个操作数的无符号类型。
到无符号整数类型的转换是明确定义的,即使所讨论的值不能用新类型表示:
[..] 如果新类型是无符号的,则通过在新类型可以表示的最大值上重复加或减 1 来转换该值,直到该值在新类型的范围内。 60
§ 6.3.1.3/2
脚注 60 只是说所描述的算术适用于数学值,而不是键入的值。
现在,使用更新后的代码
char f2_updated( int a, int b ); // called f3 in the question
事情看起来会有所不同。但是由于b
假设为非负数,并且假设INT_MAX <= UINT_MAX
你可以转换b
to an unsigned
不用担心它之后会有不同的数学值。因此你可以写
char f2_updated( int a, int b ) {
return f2(a, (unsigned)b); // cast unnecessary but to make it clear
}
再看一遍f2
表达方式2*b
进一步限制了允许的范围b
不大于UINT_MAX/2
(否则数学结果将是错误的)。
因此,只要您保持在这些范围内,一切都很好。
注意:无符号类型不会溢出,它们根据模算术“换行”。
引自 N1570(C11 工作草案)
最后一句话:
IMO 编写此函数的唯一真正合理的选择是
#include <stdbool.h>
#include <assert.h>
bool abs_bounded(int value, unsigned bound) {
assert(bound <= (UINT_MAX / 2));
/* NOTE: Casting to unsigned makes the implicit conversion that
otherwise would happen explicit. */
return ((unsigned)value + bound) <= (2 * bound);
}
使用有符号类型bound
没有多大意义,因为值的绝对值不能小于负数。abs_bounded(value, something_negative)
总是错误的。如果存在负界的可能性,那么我会在该函数之外捕获它(否则它会“太多”),例如:
int some_bound;
// ...
if ((some_bound >= 0) && abs_bounded(my_value, some_bound)) {
// yeeeha
}