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

hyp*_*gic 130 c c++ binary bit-fields

我已经看到这种模式在C&C++中使用了很多.

unsigned int flags = -1;  // all bits are true
Run Code Online (Sandbox Code Playgroud)

这是一个很好的便携式方法吗?或正在使用0xffffffff~0更好?

Joh*_*itb 152

我建议你完全按照你所展示的那样去做,因为它是最直接的.初始化-1始终有效,独立于实际符号表示,~有时会出现令人惊讶的行为,因为您必须具有正确的操作数类型.只有这样,您才能获得最高价值的unsigned类型.

有关可能出现意外的示例,请考虑以下问题:

unsigned long a = ~0u;
Run Code Online (Sandbox Code Playgroud)

它不一定存储所有位1的模式a.但它首先会创建一个包含所有位1的模式unsigned int,然后将其分配给a.当unsigned long有更多位时会发生什么,并非所有这些都是1.

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

unsigned int a = ~0; // Should have done ~0u !
Run Code Online (Sandbox Code Playgroud)

原因是~0必须反转所有位.反转将产生-1两个补码机器(这是我们需要的值!),但不会屈服于-1另一个表示.在一个补码机器上,它产生零.因此,在一个补码机器上,上面将初始化a为零.

你应该理解的是,所有这些都与价值观有关 - 而不是比特.变量用初始化.如果在初始值设定项中修改了用于初始化的变量的位,则将根据这些位生成该值.初始化a为最高可能值所需的值是-1UINT_MAX.第二个将取决于a你需要使用ULONG_MAX的类型unsigned long.但是,第一个不依赖于它的类型,这是获得最高价值的好方法.

我们不是在谈论是否-1所有位都是(它并不总是).而且我们不是在谈论是否~0所有位都是(当然它有).

但我们所说的是初始化flags变量的结果是什么.对于它,-1适用于所有类型和机器.

  • @Mark提问者关心.他问:"使用-1将所有位设置为true是否安全".这并没有询问`-1`代表什么位,也没有询问`~0'有什么位.我们可能不关心值,但编译器会这样做.我们不能忽视操作与值一起工作的事实.`~0`的*值*可能不是`-1`,但这是你需要的值.请参阅我的回答和@ Dingo的总结. (14认同)
  • "我们不是在谈论-1是否所有位都是一位(它并不总是如此).而且我们不是在讨论〜0是否所有位都是一位(当然,它有)." - 哇??? 我认为整点*是*将所有位设置为1.这就是标志的工作原理..没有?你看看*位*.谁在乎价值? (11认同)
  • 为什么-1保证转换为所有?标准是否保证了这一点? (9认同)
  • 发生的转换是它重复添加超过ULONG_MAX的一个,直到它在范围内(C TC2草案中为6.3.1.3).在C++中它是相同的,只是使用另一种形式化它的方式(模2 ^ n).这一切都归结为数学关系. (9认同)
  • @litb:cast -1肯定是获得最大无符号值的好方法,但它并不是真正描述性的; 这就是为什么存在_MAX常数的原因(在C99中添加了SIZE_MAX); 授予,C++版本`numeric_limits <size_t> :: max()`有点啰嗦,但演员阵容也是如此...... (6认同)
  • @ 0x499602D2由于litb已经解释过,signed-> unsigned conversion不涉及_bit表示_,它涉及_numeric values_(带有"模N"算术).在`unsigned int a = -1之后;``a`的值等于"-1 modulo UINT_MAX + 1"(正余数),这里是`(UINT_MAX + 1) - 1`,即`UINT_MAX`,其位表示全是1(甚至在一个补码机器上). (6认同)
  • @Adrian你能提供我的报价吗?不,你总是可以做`unsigned int flags = static_cast <unsigned int>( - 1);`如果你的编译器太挑剔了.什么都不需要禁用. (4认同)
  • 谨防那些补码表示(在一些不起眼的DSP芯片中看到). (3认同)
  • 应尽可能避免使用@AdrianMcCarthy强制转换但禁用编译器警告也不一定是好事.后者取决于您可以控制编译器警告的程度.演员在你的代码中引入了所述依赖(`unsigned long x =(unsigned int)-1;` - oops!).所以关于代码本身,省略演员肯定是你能做的最好的.我对各种编译器的经验不足以发出警告和标志. (3认同)
  • -1因为它鼓励用户禁用编译器警告. (2认同)
  • 我仍然不太明白为什么您建议使用-1,因为在补码机上-1是`11111110`(不是全1)。只有2的补码全为1。那么,为什么要推荐它呢? (2认同)

Din*_*ngo 48

  • unsigned int flags = -1; 是便携式的.
  • unsigned int flags = ~0; 不可移植,因为它依赖于二进制补码表示.
  • unsigned int flags = 0xffffffff; 不可移植,因为它采用32位整数.

如果要以C标准保证的方式设置所有位,请使用第一个.

  • @Mark:你混淆了两个操作.当然,`~0`产生一个'int`值,所有位都设置好了.但是将`int`赋给`unsigned int`并不一定*导致unsigned int具有与signed bit模式相同的位模式.只有2的补码表示才是这种情况.在1s'补码或符号 - 幅度表示上,将"int"值赋给"unsigned int"会产生不同的位模式.这是因为C++标准将signed - > unsigned conversion定义为模等价值,而不是具有相同位的值. (19认同)
  • C标准要求int值为零的符号位和所有值位为零.在一个补码之后,所有这些位都是一个.设置所有位的int的值为:符号和幅度:INT_MIN一个补码:-0二进制补码:-1所以语句"unsigned int flags = ~0;" 将指定上面的任何值匹配平台的整数表示.但是,两个补码的'-1'是唯一一个将所有标志位设置为1的方法. (15认同)
  • 你有这个倒退.它的设置标志为-1,依赖于二进制补码表示.在符号+幅度表示中减去一个仅设置两个比特:符号位和幅度的最低有效位. (11认同)
  • 〜0(即一个补码运算符)如何依赖于二的补码表示? (10认同)
  • @Stephen:同意代表.但是当一个int值被赋给unsigned int时,unsigned不会通过采用int值的内部表示来获得它的值(除了通常有效的二进制补码系统).分配给unsigned int的所有值都是模数(UINT_MAX + 1),因此无论内部表示如何,分配-1都有效. (9认同)
  • @Mark"所有位"和"所有标志位"之间没有预期的区别.他们是一样的.如果flags是无符号的,则表达式"flags = ~0"将不会始终有效.最简单的例子是在一个补充平台上.在这样的平台上,"~x == -x"对于有符号整数是正确的.因此,"〜0 == -0"为真.在该平台上,"flags =〜0"与"flags = -0"相同,后者又与"flags = 0"相同.因此,在一个补充平台上,"flags =〜0"导致没有设置位. (3认同)
  • @Donotalo:在补码系统上,将负符号 int 类型转换为无符号 int 并不简单地重新解释相同的位模式,而是生成对应于(整数值 + UNSIGNED_MAX)的任何位模式。 (3认同)
  • 我仍然对为什么 `-1` 是可移植的方式感到困惑。考虑在 1 的补码系统上的“-1”,具有“4 位”整数宽度。所以,`1 = 0001b`,`-1 = 1110b`。`unsigned int flags = -1` 应该将 `1110b` 分配给标志,不是吗? (2认同)
  • @Donotalo:你缺少的是将有符号类型转换为无符号类型的规则要求*无论位表示如何*-1都会变成值,当加到+1时,将产生零,即无符号类型的最大值.请注意,如果有一个签名变量保持-1并且一个访问内存使用(unsigned char*),则无法保证包含该变量的所有unsigned char都将设置其所有位.(unsigned whatever)( - 1)所有位设置的保证仅适用于显式强制转换,而不适用于内存叠加. (2认同)

Dou*_* T. 25

坦率地说,我认为所有的fff都更具可读性.至于它是反模式的评论,如果你真的关心所有的位都被设置/清除,我会争辩说你可能处于这样一种情况,你无论如何都要关心变量的大小,这会要求像boost这样的东西:: uint16_t等

  • +1.即使数据类型的大小大于F的#(即,您没有将所有位都设置为true),因为您明确设置了值,您至少知道哪些位是"安全的"使用".. (2认同)

ham*_*mar 17

避免上述问题的一种方法是简单地做:

unsigned int flags = 0;
flags = ~flags;
Run Code Online (Sandbox Code Playgroud)

便携式和重点.

  • 但是,这样就失去了将“ flags”声明为“ const”的能力。 (2认同)
  • 嗯,是的,我没有注意到您答案中的`u`后缀。当然可以,但是仍然存在两次指定使用的数据类型(“ unsigned”和不更大)的问题,这可能导致错误。但是,如果赋值和初始变量声明相距较远,则该错误最有可能出现。 (2认同)

Edo*_* A. 13

我不确定在C++中首先使用unsigned int for flags是一个好主意.比特怎么样?

std::numeric_limit<unsigned int>::max()更好,因为0xffffffff假设unsigned int是一个32位整数.

  • 您可以使用标准的UINT_MAX宏来缓解冗长的情况,因为无论如何您都在对unsigned int类型进行硬编码。 (2认同)
  • @Macke您可以避免使用`auto`在C ++ 11中声明类型。auto const flags = std :: numeric_limit &lt;unsigned&gt; :: max()。 (2认同)

Adr*_*thy 11

unsigned int flags = -1;  // all bits are true
Run Code Online (Sandbox Code Playgroud)

"这是一个很好的[,]便携式方法吗?"

便携式? 是的.

好? 辩论,这个线程上显示的所有混乱都证明了这一点.足够清楚,你的程序员可以毫不混淆地理解代码,这应该是我们衡量好代码的维度之一.

此外,此方法易于出现编译器警告.要在不破坏编译器的情况下忽略警告,您需要显式强制转换.例如,

unsigned int flags = static_cast<unsigned int>(-1);
Run Code Online (Sandbox Code Playgroud)

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

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

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);
Run Code Online (Sandbox Code Playgroud)

所有这些都是正确的,对你的程序员来说更明显.

使用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);
Run Code Online (Sandbox Code Playgroud)

我认为正确和明显优于简单正确.


R..*_*R.. 10

标准保证将-1转换为任何无符号类型以产生全1.使用~0U通常是坏的,因为0有类型unsigned int并且不会填充更大的无符号类型的所有位,除非你明确地写了类似的东西~0ULL.在理智的系统上,~0应该是相同的-1,但由于标准允许补码和符号/幅度表示,严格来说它不是便携式的.

当然,0xffffffff如果你知道你需要正好32位,那么写出来总是可以的,但是-1的优势在于它可以在任何环境中工作,即使你不知道类型的大小,例如可以在多种类型上工作的宏,或者如果类型的大小因实施而异.如果你知道的类型,另一种安全的方式来获得所有的人已是极限宏UINT_MAX,ULONG_MAX,ULLONG_MAX,等.

我个人总是使用-1.它总是有效,你不必考虑它.

  • @Donal:你错了.C指定在转换不适合无符号类型的值时,以模2 ^ n减少值,其中n是目标类型中的位数.这适用于有符号值和较大的无符号类型.它与二重补充无关. (6认同)
  • 他们**同样实施它,这是微不足道的; 你只需使用无符号减法运算代码而不是有符号运算符或'neg`指令.具有伪造签名算术行为的机器具有单独的有符号/无符号算术操作码.当然,一个非常好的编译器总是会忽略已签名的操作码,即使对于有符号值,也可以免费获得二进制补码. (2认同)

Mic*_*ish 5

只要你有#include <limits.h>一个包含,你就应该使用

unsigned int flags = UINT_MAX;
Run Code Online (Sandbox Code Playgroud)

如果你想要一个很长的比特,你可以使用

unsigned long flags = ULONG_MAX;
Run Code Online (Sandbox Code Playgroud)

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


Dia*_*hon 5

是.如其他答案所述,-1是最便携的; 但是,它不是非常语义并且会触发编译器警告.

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

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;
Run Code Online (Sandbox Code Playgroud)

用法:

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;
Run Code Online (Sandbox Code Playgroud)

  • 作为C程序员,像这样的代码在晚上给了我糟糕的梦想. (3认同)