使用-1作为无符号(size_t)类型的标志值

10
我曾经在一个返回类型为size_t(无符号类型)的函数中使用-1作为标志值。
一开始我没有注意到这个问题,特别是因为它并没有导致我的代码出错(我是用x == -1进行检查,而不是x < 0)。
有没有什么微妙的原因让我不能将其保留?它可能在什么情况下表现出意外行为?这种方法常用吗? ptrdiff_t不太常见,打字需要更长时间,而且它实际上也不是适当的类型,因为该函数返回一个数组的索引。
3个回答

13

-1将始终转换为最大无符号值,这是由于章节4.7 Integral conversions

如果目标类型是无符号的,则结果值是源整数对2n取模后等于最小无符号整数的值,其中n是用于表示无符号类型的位数。[注意:在二进制补码表示中,此转换是概念性的,如果没有截断,则没有位模式的变化。—注]

C99中相同引文的内容见于6.3.1.3

否则,如果新类型是无符号的,则通过重复添加或减去可以在新类型中表示的最大值加1来转换该值,直到该值处于新类型的范围内。 49)

因此我们得出结论:

-1 + (UMAX + 1)

它是:

UMAX

我理解它的工作原理,但我更想知道是否利用它是一个好主意?有很多事情你可以做,但不应该做,这让我感到有点不安,但我无法解释为什么。 - dspyz
@dspyz 你是在问是否应该用 x < 0 替换 x == -1 的检查吗?如果你想让编译器优化掉这些检查,那么是的,请继续这样做。 - Praetorian
@dspyz,这个问题并不清楚。你对这种方法有什么疑虑?它看起来很奇怪,但结果是明确定义的。 - Shafik Yaghmour
好的,我一直在思考这个可能出错的地方。当然,如果有人使用x < 0检查值,他们不会注意到它是-1(除非它已经转换为相同大小的有符号值),但是聪明的编译器会警告您尝试检查无符号类型是否<0。更重要的是,如果size_t是一个无符号整数,并且函数调用者在检查之前隐式地将返回值强制转换为long,则它将不是-1。我认为我应该显式地使用size_t.max作为哨兵值。 - dspyz
@dspyz size_t保证是unsigned类型,并且在可移植性方面,使用-1作为最大无符号值是标准保证的。当然,你也可以使用std::numeric_limits<size_t>::max(),但这是同样的事情。 - Shafik Yaghmour

4
明显的警告是对于一组元素,其大小等于可能的最大大小的情况。实际发生这种情况并成为问题原因的可能性和实用性微乎其微。
如果查看C++中的std::string类,您会注意到static std::string::npos数据成员被定义为刚好转换为std::string::size_type(实际上只是std::size_t)的-1。 这给了这个“技巧”一种先例意义,使它能够满足最小惊奇原则,这总是一件好事。
现在,直接使用-1进行比较就会出问题。与std::string情况一样,你应该确保有一个可访问的名称来确保其特殊含义。 不幸的是,C ++类型系统不够严格,无法防止用户自取灭亡,但至少遵循文档最佳实践的用户不会考虑采用不同的做法。

3

在尝试想出可能出错的方式后,我意识到存在一个危险:调用函数可能会将返回值隐式转换为更大的类型(即unsigned int转换为unsigned long long)。然后检查该值是否等于-1将会是错误的。

更安全的选择是明确使用size_t.max作为哨兵值。我总是不太放心在有符号和无符号类型之间进行转换。有时我认为更合理的方法是将所有东西都设为有符号(就像Java一样)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接