如何在C++中将负一表示为无符号字面量

4
在我的程序中,我经常使用-1(也称为UINT_MAX)作为无符号变量的特殊值。我还会与这个值进行比较。开启更高级别的警告信息后,编译器(包括VS和GCC)不喜欢这种语法。
// warning C4245: 'initializing' : conversion from 'int' to 'unsigned int', signed/unsigned mismatch
unsigned a = -1;

// warning C4146: unary minus operator applied to unsigned type, result still unsigned
unsigned b = -1U;

// fine but messy
unsigned c = unsigned(-1);

// no warning on VS, signed/unsigned comparison warning with GCC
if (a == -1)
{
  std::cout << "check\n";
}

问题1:上述语法(每个案例)是否是合法的C++代码?

问题2:我是否真的必须在使用此值分配/比较到无符号整数时到处编写unsigned(-1),还是有一种更简洁的方法不会触发编译器警告?


8
你反对使用UINT_MAX有什么原因? - Sam DeHaan
6个回答

6

答案1:

是的,它们都是合法的,尽管可以说是不愉快的C++结构。

  • -1 被解释为int字面值。这是C++语法正确的,因为标准允许隐式的int到unsigned int转换,但是编译器在更高的警告级别下使用此隐式转换时会发出警告。
  • -1U 被解释为 -(1U)。取一个无符号整数的负数是C++语法正确的,因为编译器对模(UINT_MAX + 1)执行此操作,但是编译器会发出警告来提醒您。
  • (unsigned(-1) == -1) 也是正确的语法,因为编译器将-1隐式转换为unsigned(-1)进行比较。在更高的警告级别下,它会发出警告告诉您它已经这样做了。

答案2:

有几种选项可以写或影响unsigned(-1)作为文字:

  • unsigned(-1)
  • ~0U,在补码机器上,它取0,然后否定每个位,这给出了最大的可表示无符号数。
  • UINT_MAX,它有效地被#defineunsigned(-1)
  • 如果分配或与无符号值比较,请写-1并让编译器进行隐式转换并处理警告消息。但是,应该注意的是,-1是一个int文字,只有根据其在代码中的功能才会转换为unsigned。
  • std::numeric_limits<unsigned>::max(),这是一个返回unsigned(-1)的函数。请注意,在C++11中,此函数将是constexpr,一个好的编译器应该总是将其简化为文字。VC++目前将其简化为非调试版本的unsigned(-1)。但是,它是从函数调用返回的值,而不是文字。

下一个维护这段代码的人如何知道你是指“-1”还是“最大的无符号数”? “-1”是有符号的,应该按照有符号数处理! - johnsyweb
假装我在问题的第一句话中称其为UINT_MAX。 - devtk

6

按其本质,你不能在C++或任何其他语言中将负一写成无符号文字。

你不是指-1,而是指std::numeric_limits<unsigned int>::max()

#include <iostream>
#include <limits>

int main()
{
    unsigned a = std::numeric_limits<unsigned int>::max();

    if (a == std::numeric_limits<unsigned int>::max())
    {
        std::cout << "check " << a << "\n";
    }
}

C++11标准将此函数定义为constexpr,这保证了该函数是一个编译时常量。(进一步阅读)。
如果您想避免在使用旧编译器的每次循环迭代中计算此函数,请在循环外创建一个const并进行比较。
const unsigned int magic_number = std::numeric_limits<unsigned int>::max();

    // [...]
    if (a == magic_number)

嗯,-1是编译时常量,而std::numeric_limits<unsigned int>::max()好像不是。由于我要在大循环中使用它进行比较,因此我希望能提高效率。 - devtk
我很感激您的笔记,但调用constexpr函数仍然不是字面值。我在第一句话中澄清了我所说的-1是UINT_MAX的含义。标题是为了让搜索引擎正确标记它。搜索结果显示“-1”很难处理,没有有用的信息。 - devtk

1
为避免处理int类型长度的问题,您可以使用
~0

这将给你所有的1,在一个二进制补码机器上(现在还有其他的吗?)等同于-1。但它是一个合法的无符号长整型。


你可能想在某个地方放置一个断言,以确保机器是二进制补码。虽然这种情况并不常见,但这并不需要太多的努力,而且即使只是作为一种练习,可移植性也是很好的。 - bames53
我的VS编译器显示~0是一个int,因此警告是相同的。 - devtk
“it is a legit unsigned long” 是什么意思? - Alexey Frunze

1
请查看本文末尾的讨论主题。

http://embeddedgurus.com/barr-code/2011/06/is-uint16_t-1-portable-c-code/

结论是(unsigned)(-1)是完全合法的,并且无论实际宽度或内部表示如何,都保证具有该类型的最大值。

是否这样做是个好主意是另一个问题。就我个人而言,我更喜欢Johnsyweb提供的解决方案。

至于是否总是需要强制转换,这将取决于编译器。由于您已经尝试了VS和GCC,所以似乎是这样。


0

直接使用ULONG_MAX怎么样?需要包含limits.h头文件。

或者,您也可以使用0xFFFFFFFF -- 当然要在宏或静态常量中正确定义。


1
由于这是C++,最好使用std::numeric_limits<unsigned long>::max() - loganfsmyth
UINT_MAX是一个定义(ULONG_MAX不相关),0xFFFFFFFFL是一个长整型,而不是无符号的,并且是系统特定的。 - devtk

0

我认为你不应该混淆无符号和有符号。-1 是有符号的,应该保持这种方式,也许你想经常使用 -1 是一个设计问题。有一些解决方案,比如 boost::optional。

http://www.boost.org/doc/libs/1_49_0/libs/optional/doc/html/index.html

例子:

#include <boost/optional.hpp>
#include <iostream>

boost::optional<int> find (const std::string& s, char t)
{
   for (int i = 0 ; i < s.length () ; ++ i)
   if (s [i] == t)
   return i ;
   return boost::optional<int>() ;
}

int main (int argc, char* argv[]) 
{
    std::string s = argv[1] ;
    char t = *argv[2] ;
    boost::optional<int> idx = find (s, t) ;
    if (idx)
        std::cout << "found at " << *idx << std::endl ;
    else
        std::cout << "not found" << std::endl ;

   return 0 ;
}

为了将-1作为无符号字面量进行编写,包括Boost并不是一个选项。有许多用途可以在无符号变量上使用-1,例如在std::string中的许多用途。 - devtk
1
@devtk 对于 std::string,你应该写 std::string::npos。但是也有合法的用途。例如,wcswidth() 返回一个 size_t 类型的值,但通过返回 -1 来报告错误。因此,将返回值与 (size_t)-1size_t(-1) 进行比较是正确的做法。 - bames53

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