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

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

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

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

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

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

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

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

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

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

 if (abs(-1) * -1 < 0) printf("< 0\n"); else printf(">= 0\n"); 

会打印“> = 0”,这是许多人不愿意的。 无法使用单值INT_MIN可能看起来INT_MIN

为什么它会使用无符号空格返回一个值?

让我们考虑8位有符号和无符号数。 如果你有-128,结果是未定义的……我猜stdlib不希望减慢这么多。 如果您认为您可能拥有该范围内的数字,那么您需要使用其他内容。

如果您认为签名字符中的值可能大于127,那么您就错了。

因此,值不必保持大于127的值,并且保持签名不会丢失。 如果你想将它转换为无符号,请继续。 因为它曾经是一个有符号整数,所以你再次进行签名数学的几率很高。 就我个人而言,我认为我更喜欢这种类型保持签名,因为我真的很想要处理无符号并且我没有做位操作。

但也许其他人可以从标准委员会那里挖掘一些笔记。