为什么stdlib.h的abs()系列函数返回有符号值?

cdl*_*ary 10 c math std

手册中注明了这种负面含义:

注意未定义尝试取最负整数的绝对值.

这背后的原因是什么?对于想避免未定义行为的人来说,最好的办法是什么?我是否必须采取以下措施:

unsigned uabs(signed val) {
    return val > 0
        ? val
        : (val == 1U << ((sizeof(val) * 8) - 1))
            ? -1U
            : -val;
}
Run Code Online (Sandbox Code Playgroud)

(故意hacky强调对stdlib的不满;-)

假设您有一个4位有符号值(为了便于理解).unsigned max是15,signed(正)max是7,signed(负)min是-8,所以abs(-8)不适合有符号值.当然,您可以将其表示为-8,但随后对结果进行除法和乘法不会按预期工作.

caf*_*caf 19

这个问题的真正答案在于类型提升规则.

如果我将算术运算符应用于a unsigned int和a int,则int参数将被提升为unsigned,结果也是unsigned.

如果abs()函数返回unsigned,那么当它在表达式中使用时会导致其他类型的类型提升,这会导致意外的结果.例如,这段代码:

if (abs(-1) * -1 < 0)
    printf("< 0\n");
else
    printf(">= 0\n");
Run Code Online (Sandbox Code Playgroud)

会打印"> = 0",这是许多人不喜欢的.无法使用单一值的权衡INT_MIN可能看起来不错.

  • 我可以购买这种类型的促销很复杂,你不能责怪任何人希望一切符合有价值的价值.我想如果我能及时回到与委员会争论,我会说abs应该尽可能准确,并且C程序员已经必须知道如何处理函数返回类型和表达式中的类型提升,但是我离开了我的时间机器在公共汽车上一次. (2认同)