代码之家  ›  专栏  ›  技术社区  ›  hyperlogic

使用-1将所有位设置为真是否安全?

  •  126
  • hyperlogic  · 技术社区  · 16 年前

    我见过这种模式在C&AMC+++中使用了很多。

    unsigned int flags = -1;  // all bits are true
    

    这是实现这一目标的一种好的可移植方式吗?或正在使用 0xffffffff ~0 更好?

    19 回复  |  直到 16 年前
        1
  •  151
  •   David G    12 年前

    我建议你照你所说的做,因为这是最直接的。初始化为 -1 哪个会起作用 总是 ,独立于实际符号表示,而 ~ 有时会有令人惊讶的行为,因为您必须具有正确的操作数类型。只有这样你才能得到 unsigned 类型。

    举一个可能的惊喜的例子,考虑一下这个:

    unsigned long a = ~0u;
    

    它不一定将所有位1的模式存储到 a . 但它将首先创建一个模式,其中所有位1位于 unsigned int ,然后将其分配给 . 当 unsigned long 有更多的位是不是所有的都是1。

    并考虑这一个,它将在非二的补码表示上失败:

    unsigned int a = ~0; // Should have done ~0u !
    

    原因是 ~0 必须反转所有位。反转将产生 - 1 在二的补码机上(这是我们需要的值!)但意志 产量 - 1 在另一个代表。在补码机上,它产生零。因此,在一个人的补码机上,上述内容将初始化 到零。

    你应该理解的是,这一切都是关于价值的,而不是比特。变量初始化为 价值 .如果在初始值设定项中修改用于初始化的变量的位,则将根据这些位生成值。初始化所需的值 达到最大可能值 - 1 UINT_MAX . 第二个取决于 -你需要使用 ULONG_MAX 对于一个 无符号长 . 然而,第一种方法并不取决于它的类型,而且它是获得最高价值的一种很好的方法。

    我们是 谈论是否 - 1 所有的位都是一(它不总是有)。我们是 谈论是否 ~ 0 所有的位都是1(当然是1)。

    但我们所说的是初始化的结果 flags 变量是。为此, 只有 - 1 适用于各种类型和机器。

        2
  •  46
  •   Mateen Ulhaq    13 年前
    • unsigned int flags = -1; 是便携式的。
    • unsigned int flags = ~0; 不是便携式的,因为它 依赖于两个补表示。
    • unsigned int flags = 0xffffffff; 不是便携式的,因为 它假设32位整数。

    如果您希望以C标准保证的方式设置所有位,请使用第一个位。

        3
  •  24
  •   Doug T.    16 年前

    坦率地说,我认为所有的FFF都更可读。关于它是一个反模式的注释,如果您真的关心所有的位都被设置/清除,那么我认为您可能处于一种不管怎样都关心变量大小的情况下,这种情况需要一些诸如boost::uint16_等的东西。

        4
  •  17
  •   hammar Muthumani    16 年前

    避免上述问题的方法是:

    unsigned int flags = 0;
    flags = ~flags;
    

    便携的,切中要害的。

        5
  •  13
  •   Akhil Jain Thedaego    12 年前

    我不确定在C++中首先使用标志的无符号int是个好主意。比特集之类的呢?

    std::numeric_limit<unsigned int>::max() 更好是因为 0xffffffff 假设无符号int是32位整数。

        6
  •  10
  •   Adrian McCarthy    12 年前
    unsigned int flags = -1;  // all bits are true
    

    “这是一个很好的[,]便携式方式来实现这一点吗?”

    便携? 是的 .

    好吗? 有争议的 从这条线上显示的所有混淆可以证明。清楚地表明您的程序员可以理解代码而不产生混淆,这应该是我们衡量良好代码的维度之一。

    而且,这种方法很容易 编译器警告 . 要在不破坏编译器的情况下删除警告,需要显式转换。例如,

    unsigned int flags = static_cast<unsigned int>(-1);
    

    显式强制转换要求您注意目标类型。如果您关注的是目标类型,那么您自然会避免其他方法的陷阱。

    我的建议是注意目标类型并确保没有隐式转换。例如:

    unsigned int flags1 = UINT_MAX;
    unsigned int flags2 = ~static_cast<unsigned int>(0);
    unsigned long flags3 = ULONG_MAX;
    unsigned long flags4 = ~static_cast<unsigned long>(0);
    

    所有这些都是 正确和更明显 给你的程序员同事。

    用C++ 11 我们可以使用 auto 为了使这些更简单:

    auto flags1 = UINT_MAX;
    auto flags2 = ~static_cast<unsigned int>(0);
    auto flags3 = ULONG_MAX;
    auto flags4 = ~static_cast<unsigned long>(0);
    

    我认为正确和明显比简单的正确更好。

        7
  •  9
  •   R.. GitHub STOP HELPING ICE    14 年前

    将-1转换为任何无符号类型是 标准保证 导致所有的结果。使用 ~0U 通常是坏的,因为 0 有类型 unsigned int 并且不会填充更大的无符号类型的所有位,除非显式地编写类似的内容 ~0ULL . 在健全的系统上, ~0 应该与 -1 但由于该标准允许一个补码和符号/幅度表示,严格来说,它是不可移植的。

    当然,写出来总是可以的 0xffffffff 如果您知道您只需要32位,但是-1的优点是它可以在任何上下文中工作,即使您不知道类型的大小,例如在多个类型上工作的宏,或者类型的大小因实现而异。如果您知道类型,另一种安全的获取所有类型的方法是限制宏 UINT_MAX , ULONG_MAX , ULLONG_MAX 等。

    我个人总是用-1。它总是有效的,你不必去想它。

        8
  •  5
  •   Timotheos    11 年前

    对。如其他答案所述, -1 是最可移植的;但是,它不是很语义化,会触发编译器警告。

    要解决这些问题,请尝试以下简单帮助器:

    static const struct All1s
    {
        template<typename UnsignedType>
        inline operator UnsignedType(void) const
        {
            return static_cast<UnsignedType>(-1);
        }
    } ALL_BITS_TRUE;
    

    用途:

    unsigned a = ALL_BITS_TRUE;
    uint8_t  b = ALL_BITS_TRUE;
    uint16_t c = ALL_BITS_TRUE;
    uint32_t d = ALL_BITS_TRUE;
    uint64_t e = ALL_BITS_TRUE;
    
        9
  •  5
  •   Michael Norrish    9 年前

    只要你有 #include <limits.h> 作为其中一项,您应该使用

    unsigned int flags = UINT_MAX;
    

    如果你想要一长串的比特,你可以用

    unsigned long flags = ULONG_MAX;
    

    无论有符号整数是如何实现的,这些值都保证将结果的所有值位设置为1。

        10
  •  3
  •   Akhil Jain Thedaego    12 年前

    我不会做-1件事。这是相当不直观的(至少对我来说)。将有符号数据分配给无符号变量似乎违背了事物的自然顺序。

    在你的情况下,我总是用 0xFFFF . (当然,使用正确数量的fs作为可变大小。)

    [顺便说一句,我很少看到现实世界代码中的-1技巧。]

    另外,如果您真的关心一个可用的中的单个位,那么最好开始使用固定宽度 uint8_t , uint16_t , uint32_t 类型。

        11
  •  2
  •   Nathan Fellman    16 年前

    在英特尔的IA-32处理器上,可以将0xffffffff写入64位寄存器并获得预期的结果。这是因为ia32e(ia32的64位扩展)只支持32位立即数。在64位指令中,32位立即数是 符号扩展 到64位。

    以下是非法的:

    mov rax, 0ffffffffffffffffh
    

    以下将64 1s置于RAX中:

    mov rax, 0ffffffffh
    

    为了完整起见,下面将32 1s放在rax(aka eax)的下部:

    mov eax, 0ffffffffh
    

    事实上,当我想将0xffffffff写入64位变量时,程序失败了,取而代之的是0xffffffffffffffffff。在C中,这是:

    uint64_t x;
    x = UINT64_C(0xffffffff)
    printf("x is %"PRIx64"\n", x);
    

    结果是:

    x is 0xffffffffffffffff
    

    我本想把它作为评论贴在所有回答上,说0xffffffff假设32位,但是很多人都回答了,我想我会把它作为一个单独的答案添加。

        12
  •  2
  •   James Hopkin    16 年前

    有关这些问题的非常清楚的解释,请参阅Litb的答案。

    我的不同意见是,严格地说,这两种情况都没有保证。我不知道任何一种架构,它不代表所有位集的“1小于2的位数的幂”的无符号值,但标准实际上是这样说的(3.9.1/7加上注44):

    积分类型的表示应使用纯二进制计数系统定义值。[注44:]使用二进制数字0和1的整数的位置表示法,其中由连续位表示的值是相加的,以1开头,并乘以2的连续整数幂,可能具有最高位置的位除外。

    这就使得其中一个比特可能是任何东西。

        13
  •  1
  •   Drew Hall    16 年前

    实际上:是的

    理论上:没有。

    -1=0xffffffff(或者平台上的int的任何大小)只对two的补码算法有效。实际上,它是可行的,但是有一些遗留的机器(IBM大型机等),在那里您得到了一个实际的符号位,而不是一个二的补码表示。你提出的~0解决方案应该适用于任何地方。

        14
  •  1
  •   supercat    12 年前

    虽然 0xFFFF (或) 0xFFFFFFFF 等)可能更容易阅读,否则它会破坏可移植代码的可移植性。例如,考虑一个库例程来计算数据结构中有多少项设置了特定的位(调用方指定的确切位)。程序可能完全不可知位代表什么,但仍然需要有一个“所有位设置”常量。在这种情况下,-1将远远优于十六进制常量,因为它可以处理任何位大小。

    另一种可能性,如果 typedef 值用于位掩码,将使用~(bitmaskType)0;如果位掩码恰好是16位类型,则该表达式将只设置16位(即使“int”否则为32位),但由于16位是所有必需的,因此情况应该很好。 假如 实际上,它在类型转换中使用了适当的类型。

    顺便说一下,形式的表达 longvar &= ~[hex_constant] 如果十六进制常量太大而无法放入 int ,但将适合 unsigned int . 如果一个 int 是16位,那么 longvar &= ~0x4000; longvar &= ~0x10000 ;将清除 longvar 但是 longvar &= ~0x8000; 将清除第15位及以上所有位。适合的值 int 将对类型应用补码运算符 int ,但结果将符号扩展到 long ,设置高位。价值观太大 无符号整型 将对类型应用补码运算符 长的 . 但是,这些大小之间的值将对类型应用补码运算符 无符号整型 ,然后将其转换为类型 长的 无符号扩展。

        15
  •  1
  •   David Stone    12 年前

    正如其他人提到的那样,-1是创建整数的正确方法,该整数将转换为无符号类型,所有位都设置为1。然而,C++中最重要的是使用正确的类型。因此,问题的正确答案(包括问题的答案)是:

    std::bitset<32> const flags(-1);
    

    这将始终包含所需的确切位数。它构造了一个 std::bitset 所有位设置为1,原因与其他答案中提到的相同。

        16
  •  0
  •   Zifre    16 年前

    它当然是安全的,因为-1将始终设置所有可用位,但我更喜欢~0。-1只是对一个 unsigned int . 0xFF …不是很好,因为它取决于类型的宽度。

        17
  •  0
  •   Hank Gay    14 年前

    我说:

    int x;
    memset(&x, 0xFF, sizeof(int));
    

    这将始终为您提供所需的结果。

        18
  •  0
  •   Community CDub    8 年前

    利用这样一个事实,即为无符号类型将所有位分配给一个位等于为给定类型获取最大可能值,
    把问题的范围扩大到所有人 未签名的 整数类型:

    Assigning -1 works for any unsigned integer type C和C++两种语言(无符号INT,UINT8YT,UIN1616T等)。

    作为另一种选择,对于C++,你可以:

    1. 包括 <limits> 使用 std::numeric_limits< your_type >::max()
    2. 写一篇 custom templated function (这也允许进行一些健全性检查,例如,如果目标类型确实是无符号类型)

    其目的可能是增加更多的清晰度,如分配 -1 总是需要一些解释性的评论。

        19
  •  -6
  •   ankit sablok    14 年前

    是的,所示的表示是非常正确的,就像我们这样做一样,反过来,u轮将需要一个运算符来反转所有的位,但是在这种情况下,如果我们考虑到机器中整数的大小,逻辑是非常简单的。

    例如,在大多数机器中,一个整数是2字节=16位,它可以保存的最大值是2^16-1=65535 2^16=65536

    0% 65536=0 -1%65536=65535,将其腐蚀到1111………1,所有位都设置为1(如果我们考虑剩余类mod 65536) 因此,这是非常直接的。

    我猜

    不,如果你考虑到这个概念,它是完美的晚餐,为未签名的整数,它实际上解决了

    只需检查以下程序片段

    int() {

    unsigned int a=2;
    
    cout<<(unsigned int)pow(double(a),double(sizeof(a)*8));
    
    unsigned int b=-1;
    
    cout<<"\n"<<b;
    
    getchar();
    
    return 0;
    

    }

    对于4字节整数,b=4294967295,whcih为-1%2^32的答案

    因此对于无符号整数是完全有效的

    如有任何差异,请提交报告。