【问题标题】:Is it safe to use -1 to set all bits to true?使用 -1 将所有位设置为真是否安全?
【发布时间】:2010-10-22 23:51:06
【问题描述】:

我已经看到这种模式在 C 和 C++ 中被大量使用。

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

这是实现此目的的一种很好的便携方式吗?还是使用0xffffffff~0 更好?

【问题讨论】:

  • 我不明白,你能解释一下吗?
  • 我认为代码的意思是否清楚是最重要的问题。尽管-1 将始终有效,但在它之后需要注释的事实表明它不是清晰的代码。如果变量是标志的集合,为什么要给它分配一个整数?它的类型可能是整数,但在语义上肯定不是整数。你永远不会增加或增加它。所以我会使用0xffffffff 不是为了可移植性或正确性,而是为了清晰。
  • @CamJackson 注释 不是,任何编写 C 代码的人都可能熟悉值的表示方式。
  • 该问题最初被正确标记为 C 和 C++。这些语言可能会有所不同,因为 C++ 提出了要求二进制补码的建议。也就是说,这并没有改变-1 仍然是两种语言的可移植和向后兼容解决方案这一事实,但它可能会影响其他答案中的一些推理。

标签: c++ c binary bit-fields


【解决方案1】:

我建议您完全按照您的说明进行操作,因为它是最直接的方法。初始化为-1,它将始终工作,独立于实际的符号表示,而~ 有时会出现令人惊讶的行为,因为您必须拥有正确的操作数类型。只有这样您才能获得unsigned 类型的最高值。

关于可能的惊喜的示例,请考虑以下示例:

unsigned long a = ~0u;

它不一定会将所有位为 1 的模式存储到 a 中。但它会首先在unsigned int 中创建一个所有位为1 的模式,然后将其分配给a。当unsigned long 有更多位时会发生什么,并不是所有位都是 1。

考虑一下这个,它在非二进制补码表示上会失败:

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

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

您应该理解的是,这一切都与价值有关,而不是位。该变量使用 value 进行初始化。如果在初始化程序中修改了用于初始化的变量的位,则将根据这些位生成值。将a 初始化为可能的最高值所需的值是-1UINT_MAX。第二个将取决于a 的类型 - 您需要使用ULONG_MAX 来表示unsigned long。但是,第一个不依赖于它的类型,这是获得最高值的好方法。

我们在谈论-1 是否所有位都为一(并非总是如此)。我们不是在谈论~0 是否所有位都为一(当然,它有)。

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

【讨论】:

  • 为什么保证-1 会转换为全1?标准能保证吗?
  • 发生的转换是它重复地比 ULONG_MAX 多加一,直到它在范围内(C TC2 草案中的 6.3.1.3)。在 C++ 中它是相同的,只是使用另一种形式化它(模 2^n)。这一切都归结为数学关系。
  • @litb:强制转换 -1 无疑是获得最大无符号值的好方法,但它并不是真正的描述性;这就是存在 _MAX 常量的原因(在 C99 中添加了 SIZE_MAX);诚然,C++ 版本 numeric_limits<size_t>::max() 有点啰嗦,但演员也是如此......
  • "我们不是在谈论 -1 是否所有位都为 1(它并不总是如此)。我们也不是在谈论 ~0 是否所有位都为 1(当然,它有)。” ——什么???我认为整点将所有位设置为1。这就是标志的工作方式..不是吗??你看看。谁在乎价值?
  • @Mark 提问者关心。他问“使用 -1 将所有位设置为 true 是否安全”。这不问-1 代表什么位,也不问~0 有什么位。我们可能不关心值,但编译器关心。我们不能忽视这样一个事实,即操作使用值和值。 ~0 可能不是 -1,但这是您需要的值。查看我的回答和@Dingo 的总结。
【解决方案2】:
  • unsigned int flags = -1; 是便携式的。
  • unsigned int flags = ~0; 不可移植,因为它 依赖于二进制补码表示。
  • unsigned int flags = 0xffffffff; 不可移植,因为 它假定为 32 位整数。

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

【讨论】:

  • ~0(即补码运算符)如何依赖补码表示?
  • 你有这个倒退。其将标志设置为 -1,这依赖于二进制补码表示。在符号+幅度表示中减一仅设置了两个位:符号位和幅度的最低有效位。
  • C 标准要求 int 值为零有其符号位,并且所有值位都为零。在一个补码之后,所有这些位都是一。设置所有位的 int 的值是: 符号和大小:INT_MIN 一个补码:-0 二进制补码:-1 所以语句“unsigned int flags = ~0;”将分配上面与平台的整数表示匹配的任何值。但是二进制补码的“-1”是将所有标志位设置为 1 的唯一一个。
  • @Stephen:同意这种表述。但是当一个 int 值被分配给一个 unsigned int 时,unsigned 不会通过采用 int 值的内部表示来获得它的值(除了在通常有效的二进制补码系统中)。分配给无符号整数的所有值都是模数 (UINT_MAX + 1),因此无论内部表示如何,分配 -1 都有效。
  • @Mark:你混淆了两个操作。当然,~0 会产生一个所有位都设置的int 值。但是将int 分配给unsigned int 并不必然导致 unsigned int 具有与有符号位模式相同的位模式。只有使用 2 的补码表示,情况总是如此。在 1 的补码或符号幅度表示中,将负的 int 值分配给 unsigned int 会导致不同的位模式。这是因为 C++ 标准将有符号 -> 无符号转换定义为模相等的值,而不是具有相同位的值。
【解决方案3】:

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

【讨论】:

  • 在很多情况下您并不在意,但它们很少见。例如。通过将 N 位数据集分解为 sizeof(unsigned)*CHAR_BIT 位的块来处理 N 位数据集的算法。
  • +1。即使数据类型的大小大于 F 的数量(即,您没有完全将所有位设置为 true),由于您明确设置了值,您至少知道哪些位是“安全的”使用”..
【解决方案4】:

避免上述问题的一种方法是:

unsigned int flags = 0;
flags = ~flags;

便携且切中要害。

【讨论】:

  • 但是你失去了将flags声明为const的能力。
  • @DavidStone unsigned int const flags = ~0u;
  • @Zoidberg'-- 这无法在二进制补码以外的系统上工作。例如,在符号幅度系统上,~0 是一个所有位都设置为 1 的整数,但是当您随后将 int 分配给 unsigned 变量 flags 时,您将执行从 @ 的值转换987654329@(假设是 32 位 int)到 (-2**31 % 2**32) == 2**31,这是一个整数,除了第一个设置为 1 之外的所有位。
  • 这也是这个答案很危险的一个原因。 @Zoidberg'-- 的答案似乎是相同的,但实际上并非如此。但是,作为一个阅读代码的人,我必须考虑一下,才能理解为什么您需要两个步骤来初始化它,并且可能很想将其更改为一个步骤。
  • 啊,是的,我没有注意到您回答中的 u 后缀。这当然可以,但仍然存在两次指定您使用的数据类型(unsigned 且不大于)的问题,这可能会导致错误。但是,如果赋值和初始变量声明相距较远,则最有可能出现错误。
【解决方案5】:
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);

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

【讨论】:

  • +1 用于提供最好的可移植性、更明确和可读性更好的替代 IMO:~static_cast(0)
【解决方案6】:

我不确定在 C++ 中首先使用 unsigned int 作为标志是一个好主意。 bitset 之类的呢?

std::numeric_limit&lt;unsigned int&gt;::max() 更好,因为0xffffffff 假定 unsigned int 是 32 位整数。

【讨论】:

  • 我喜欢这个因为它的标准,但是它太罗嗦了,它会让你两次声明类型。使用 ~0 可能更安全,因为 0 可以是任何整数类型。 (虽然我知道它闻起来太多 C。)
  • 罗嗦的事实可以看作是一个优势。但我也喜欢~0。
  • 您可以使用标准的 UINT_MAX 宏来减轻冗长,因为无论如何您都在硬编码 unsigned int 类型。
  • @Macke 您可以避免在 C++11 中使用 auto 声明类型。 auto const flags = std::numeric_limit&lt;unsigned&gt;::max().
【解决方案7】:

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

当然,如果你知道你需要正好 32 位,写出0xffffffff 总是可以的,但是 -1 的优点是它可以在任何上下文中工作,即使你不知道类型的大小,例如适用于多种类型的宏,或者类型的大小因实现而异。如果你知道类型,另一种获得全一的安全方法是限制宏UINT_MAXULONG_MAXULLONG_MAX 等。

就我个人而言,我总是使用-1。它总是有效的,您不必考虑它。

【讨论】:

  • FWIW,如果我的意思是“所有 1 位”,我会使用 ~(type)0(当然,填右 type)。铸造零仍然会导致零,所以这很清楚,并且否定目标类型中的所有位是非常明确的定义。不过,我实际上并不经常需要这种操作。 YMMV。
  • @Donal:你完全错了。 C 指定,当将不适合无符号类型的值转换为无符号类型时,这些值以 2^n 为模减少,其中 n 是目标类型中的位数。这适用于有符号值和更大的无符号类型。它与二进制补码无关。
  • 他们实现它是一样的,这是微不足道的;您只需使用无符号减法操作码,而不是有符号或 neg 指令。具有虚假有符号算术行为的机器具有单独的有符号/无符号算术操作码。当然,一个非常好的编译器总是会忽略带符号的操作码,即使是带符号的值,从而免费获得二进制补码。
  • @R.:var = ~(0*var) 的情况将失败,因为var 是一个比int 更窄的无符号类型。也许var = ~(0U*var)? (不过,我个人还是更喜欢-1)。
  • 这个答案比 Johannes Schaub 的要清楚得多。但是,将负文字整数分配给没有强制转换的无符号类型通常会导致编译器警告。抑制警告需要强制转换,这意味着无论如何都必须注意目标类型,因此您最好使用 UINT_MAX 或 ULONG_MAX 并保持清晰,而不是依赖标准中的微小细节,这显然会使您的许多程序员同行感到困惑.
【解决方案8】:

只要您有 #include &lt;limits.h&gt; 作为您的包含之一,您就应该使用

unsigned int flags = UINT_MAX;

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

unsigned long flags = ULONG_MAX;

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

【讨论】:

  • 您建议的常量实际上是在 limits.h 中定义的 - stdint.h 包含其他整数类型的限制(固定大小的整数,intptr_t,...)
【解决方案9】:

是的。正如其他答案中提到的,-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;

用法:

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;

【讨论】:

  • 作为一个 C 程序员,这样的代码让我晚上做噩梦。
  • 当您在ALL_BITS_TRUE ^ a 这样的上下文中使用它时会发生什么,其中a 是一个有符号整数?类型保持有符号整数,位模式(对象表示)取决于目标是否为 2 的补码。
  • 不,ALL_BITS_TRUE ^ a 给出编译错误,因为ALL_BITS_TRUE 不明确。但是,它可以像uint32_t(ALL_BITS_TRUE) ^ a 一样使用。您可以在cpp.sh 上自己尝试:) 现在我会在operator 中添加static_assert(std::is_unsigned&lt;UnsignedType&gt;::value, "This is designed only for unsigned types");,以确保用户不会尝试使用int(ALL_BITS_TRUE)。我会更新答案。
【解决方案10】:

在 Intel 的 IA-32 处理器上,可以将 0xFFFFFFFF 写入 64 位寄存器并获得预期结果。这是因为 IA32e(IA32 的 64 位扩展)仅支持 32 位立即数。在 64 位指令中,32 位立即数被 符号扩展 到 64 位。

以下内容是非法的:

mov rax, 0ffffffffffffffffh

以下将 64 个 1 放入 RAX:

mov rax, 0ffffffffh

为了完整起见,以下将 32 个 1 放在 RAX(又名 EAX)的下部:

mov eax, 0ffffffffh

事实上,当我想将 0xffffffff 写入 64 位变量时,程序失败了,而我得到了 0xffffffffffffffff。在 C 中,这将是:

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

结果是:

x is 0xffffffffffffffff

我想将此作为评论发布为对所有说 0xFFFFFFFF 假定为 32 位的答案的评论,但有这么多人回答它,我想我会将其添加为单独的答案。

【讨论】:

  • 恭喜,您发现了编译器错误!
  • 这是否被记录为错误?
  • 假设UINT64_C(0xffffffff) 扩展为0xffffffffuLL 之类的东西,这绝对是一个编译器错误。 C 标准主要讨论0xffffffff 表示的值是 4294967295(不是 36893488147419103231),并且看不到任何到有符号整数类型的转换。
【解决方案11】:

请参阅 litb 的答案以获得对问题的非常清楚的解释。

我的不同意见是,非常严格地说,这两种情况都不能保证。我不知道有任何体系结构不代表所有位设置的无符号值“小于 2 的位数的幂”,但这是标准实际所说的(3.9.1/7 加注44):

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

这使得其中一个位可以是任何东西。

【讨论】:

  • 一般来说,我们不能确定填充位的值。如果我们愿意,那么我们可能会处于危险之中,因为我们可以为它们生成一个陷阱表示(并且它可以发出信号)。但是,std 要求 unsigned char 没有填充位,并且在 c++ 标准中的 4.7/2 中说将整数转换为无符号类型,生成的无符号变量的值是与源整数值一致的最小值, (模 2^n,n==无符号类型的位数)。然后 (-1)==((2^n)-1) (mod 2^n) 。 2^n-1 的所有位都设置在纯二进制编号系统中。
  • 如果我们真的希望在无符号类型的对象表示中所有位都为 1,我们需要 memset。但是我们可以由此生成一个陷阱表示:(无论如何,一个实现可能没有理由丢掉它们的无符号整数,所以它将使用它来存储它的值。但是你有一个很好的观点——没有什么可以阻止的我认为有一些愚蠢的无意义位的解释(除了 in char/signed char/unsigned char,不能有这些)。当然 +1 :)
  • 最后,我认为标准可以更清楚地说明它在 4.7/2 中所指的表示形式。如果它指的是对象表示,那么就没有填充位的地方了(我看到人们像我认为没有任何问题的那样争论)。但我认为它谈论的是值表示(因为无论如何 4.7/2 中的所有内容都是关于值的 - 然后填充位可能嵌套在值位旁边。
  • 标准似乎非常清楚地考虑了“2 的补码、1 的补码和有符号的幅度”表示,但不想排除任何事情。关于捕获表示的有趣点。据我所知,就标准而言,我引用的位是“纯二进制计数系统”的定义 - 最后的“除外”位实际上是我对是否保证强制转换 -1 的唯一疑问工作。
【解决方案12】:

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

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

[顺便说一句,我很少看到在实际代码中使用 -1 技巧。]

此外,如果您真的关心变量中的各个位,最好开始使用固定宽度的uint8_tuint16_tuint32_t 类型。

【讨论】:

    【解决方案13】:

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

    如果将typedef 值用于位掩码,另一种可能性是使用~(bitMaskType)0;如果位掩码恰好是 16 位类型,则该表达式将仅设置 16 位(即使 'int' 否则为 32 位),但由于 16 位将是所有必需的,所以应该没问题 前提是在类型转换中实际使用了适当的类型。

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

    【讨论】:

      【解决方案14】:

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

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

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

      【讨论】:

        【解决方案15】:

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

        【讨论】:

        • " 0xFF... 不好,因为它取决于类型的宽度" 我认为这是唯一合理的方法。您应该清楚地定义程序中每个标志/位的含义。因此,如果您定义使用最低 32 位来存储标志,则应限制自己使用这 32 位,无论 int 的实际大小是 32 还是 64。
        【解决方案16】:

        实际上:是的

        理论上:不会。

        -1 = 0xFFFFFFFF(或您平台上的任何大小的 int)仅适用于二进制补码算法。在实践中,它会起作用,但是那里有旧机器(IBM 大型机等),你有一个实际的符号位而不是二进制补码表示。您提出的 ~0 解决方案应该适用于任何地方。

        【讨论】:

        • 我也说过。但后来我意识到我错了,因为 -1 signed 总是在转换规则下转换为 max_value unsigned,无论值表示如何。至少,它在 C++ 中是这样,我手头没有 C 标准。
        • 有歧义。 -1 不是 0xFFFFFFFF。但如果转换为无符号整数(具有 32 位),则 -1 为 0xFFFFFFFF。我认为这就是让这个讨论变得如此困难的原因。很多人在谈论这些位串时的想法截然不同。
        • 没有。 -1 无处不在,~0 在单补机器上会失败
        【解决方案17】:

        我说:

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

        这总是会给你想要的结果。

        【讨论】:

        • 不适用于 9 位字符的系统!
        【解决方案18】:

        利用将所有位分配给一个无符号类型的事实等价于为给定类型获取最大可能值,
        并将问题的范围扩展到所有 unsigned 整数类型:

        Assigning -1 works for any unsigned integer type(无符号整数、uint8_t、uint16_t 等)适用于 C 和 C++。

        作为替代方案,对于 C++,您可以:

        1. 包括&lt;limits&gt; 并使用std::numeric_limits&lt; your_type &gt;::max()
        2. 写一个custom templated function(这也可以进行一些完整性检查,即目标类型是否真的是无符号类型)

        目的可能是增加清晰度,因为分配-1 总是需要一些解释性注释。

        【讨论】:

        • “利用这样一个事实,即为无符号类型分配所有位相当于为给定类型取最大可能值”也许我在这里有点迂腐,但获得了清晰由于约翰内斯提到的几点,这里可能会受到质疑:价值与比特星座/表示。不太熟悉无符号整数值的位表示(填充位......)的人,标准可能也必须在这方面三思而后行,因为他必须为 -1 方法做。
        【解决方案19】:

        一种使含义更明显但又避免重复类型的方法:

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

        【讨论】:

          【解决方案20】:

          另外强调一下,为什么 Adrian McCarthy 的方法可能是自 C++11 以来在标准一致性、类型安全/显式清晰性和减少可能的歧义之间的折衷方面的最佳解决方案:

          unsigned int flagsPreCpp11 = ~static_cast<unsigned int>(0);
          auto flags = ~static_cast<unsigned int>(0); // C++11 initialization
          predeclaredflags = ~static_cast<decltype(predeclaredflags)>(0); // C++11 assignment to already declared variable
          

          我将在下面详细解释我的偏好。正如约翰内斯完全正确地提到的那样,这里激怒的根本原因是关于值与根据位表示语义以及我们正在谈论的确切类型(分配的值类型与可能的编译时间积分常量的类型)的问题。由于没有标准的内置机制来明确确保对于 OP 关于无符号整数值的具体用例将所有位设置为 1,因此很明显,这里不可能完全独立于值语义(std::bitset是一个常见的纯位层引用容器,但问题一般是关于无符号整数)。但我们或许可以减少这里的歧义。

          “更好”的符合标准的方法的比较:

          OP的方式:

          unsigned int flags = -1;
          

          专业人士:

          • 是“成立的”并且很短
          • 就“自然”位值表示的取模角度而言非常直观
          • 例如,可以将目标无符号类型更改为无符号长,而无需任何进一步的调整

          缺点:

          • 至少初学者可能不确定是否符合标准(“我必须关心填充位吗?”)。
          • 违反类型范围(以较重的方式:有符号与无符号)。
          • 仅从代码中看不到任何位语义关联。

          通过定义引用最大值:

          unsigned int flags = UINT_MAX;
          

          这规避了 -1 方法的有符号无符号转换问题,但引入了几个新问题:如果您想将目标类型更改为无符号长整数,则必须再次查看这里两次。在这里,必须确定这样一个事实,即最大值导致标准将所有位设置为 1(以及再次填充位问题)。位语义在这里也不是很明显,直接从代码中再看一遍。

          更明确地引用最大值:

          auto flags = std::numeric_limits<unsigned int>::max();
          

          在我看来,这是更好的最大值方法,因为它是免费的宏/定义,并且对所涉及的类型是明确的。但是关于方法类型本身的所有其他问题仍然存在。

          Adrian 的方法(以及为什么我认为它是 C++11 之前和之后的首选方法):

          unsigned int flagsPreCpp11 = ~static_cast<unsigned int>(0);
          auto flagsCpp11 = ~static_cast<unsigned int>(0);
          

          专业人士:

          • 仅使用最简单的整数编译时间常数:0。因此不必担心进一步的位表示或(隐式)强制转换。从直观的角度来看,我认为我们都可以同意这样一个事实,即 0 的位表示通常比最大值更清晰,不仅仅是无符号积分。
          • 不涉及类型歧义,无需进一步查找有疑问。
          • 此处通过补码 ~ 涉及显式位语义。所以从代码中可以很清楚地看出意图是什么。而且,补码适用于哪种类型和类型范围也非常明确。

          缺点:

          例如,如果分配给一个成员,那么您很可能会与 C++11 之前的类型不匹配:

          类中的声明:

          unsigned long m_flags;
          

          在构造函数中初始化:

          m_flags(~static_cast<unsigned int>(0))
          

          但是从 C++11 开始,使用 decltype + auto 可以很好地防止大多数可能出现的问题。并且这些类型不匹配的场景中的一些(例如在接口边界上)对于 -1 方法也是可能的。

          用于预声明变量的强大的最终 C++11 方法:

          m_flags(~static_cast<decltype(m_flags)>(0)) // member initialization case
          

          因此,在全面了解此处所有方法的优缺点的权重后,我推荐此方法作为首选方法,最迟自 C++11 以来。

          更新:感谢 Andrew Henle 的提示,我删除了关于其可读性的声明,因为这可能是一个过于主观的声明。但我仍然认为,它的可读性至少不比大多数最大值方法或通过编译时积分/文字明确提供最大值的方法差,因为 static_cast-usage 也“建立”并内置于定义/宏,甚至是标准库。

          【讨论】:

          • 至少是自 C++11 以来在标准一致性、可读性、类型安全/显式清晰度和减少可能的歧义方面的最佳解决方案 可读性?!?!如果那是“可读的”,我不想看到不可读的。
          • @AndrewHenle,是的,有人可能会质疑这一方面,但我想就所有相关点展示最佳折衷方案。可读性不仅仅是数量上的问题,也是隐含性的问题。例如,旧的 C 风格的“通用”演员表风格 (T) 变量乍一看是可读的,但从表面之下实际发生的情况来看是可读的?事情通常不那么可读,因为它们乍一看可能会有疑问......也许我应该用明确可理解的替换可读......
          【解决方案21】:

          是的,所显示的表示非常正确,就像我们以相反的方式进行操作一样,您将需要运算符来反转所有位,但是在这种情况下,如果我们考虑机器中整数的大小,则逻辑非常简单

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

          0%65536=0 -1%65536=65535 对应于 1111.............1 并且所有位都设置为 1(如果我们考虑残基类 mod 65536) 因此它非常简单。

          我猜

          不,如果你考虑这个概念,它非常适合无符号整数,它实际上可以解决

          只需检查以下程序片段

          int main() {

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

          }

          b = 4294967295 的答案在 4 字节整数上是 -1%2^32

          因此它对无符号整数完全有效

          如有任何差异请报告

          【讨论】:

          • 两个 cmets:首先,你对“大多数”机器上的整数大小完全错误。其次,你的 txt 很难 2 读到 2 你我们的总和类型的 secrit 语言。请简单的英语
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-10-19
          • 2023-02-06
          • 2016-01-27
          • 2013-08-26
          • 1970-01-01
          • 2019-12-06
          • 1970-01-01
          相关资源
          最近更新 更多