使用-1将所有位设置为true是否安全?
我已经看到这种模式在C&C ++中使用了很多。
unsigned int flags = -1; // all bits are true
这是一个很好的便携式的方式来完成这个? 或者是使用0xffffffff
或0xffffffff
更好?
我build议你完全按照你所说的去做,因为这是最直接的。 初始化为-1
将始终工作,独立于实际的符号表示,而~
有时会有令人惊讶的行为,因为你将不得不有正确的操作数types。 只有这样你才能得到unsigned
types的最高值。
举一个可能的惊喜的例子,考虑一下:
unsigned long a = ~0u;
它不一定将所有比特1的模式存储到a
。 但它将首先创build一个unsigned int
所有位为1的模式,然后将其分配给a
。 当unsigned long
有更多位时会发生什么,并不是所有的都是1。
并考虑这个,这将在一个非二的补码表示失败:
unsigned int a = ~0; // Should have done ~0u !
原因是〜0必须反转所有位。 反过来,在二进制补码机器(这是我们需要的值!)上将产生-1
,但在另一个表示上不会产生-1
。 在补码机上,它会产生零。 因此,在一台补码机上,上面将初始化为零。
你应该明白的是,这一切都是关于价值 – 而不是一点点。 variables用一个值初始化。 如果在初始化器中修改用于初始化的variables的位,则会根据这些位生成值。 您需要将初始化为最高可能值的值为-1
或UINT_MAX
。 第二个将取决于types – 您将需要使用ULONG_MAX
作为unsigned long
。 然而,第一个不会取决于它的types,这是获得最高价值的好方法。
我们不是在讨论-1
是否全部是第一位(它并不总是)。 而且我们不是在讨论〜0是否全部是第一(当然是)。
但是我们所说的是初始化flags
variables的结果。 而对于它, 只有-1
会适用于所有types和机器。
-
unsigned int flags = -1;
是便携式的。 -
unsigned int flags = ~0;
是不可移植的,因为它依赖于二进制补码表示。 -
unsigned int flags = 0xffffffff;
不可移植,因为它假定32位整数。
如果你想以C标准保证的方式设置所有的位,使用第一个。
坦率地说,我认为所有的fff更具可读性。 至于它是一个反模式的评论,如果你真的关心所有的位被设置/清除,我会争辩说,你可能在你关心variables的大小的情况下,这将需要像boost :: uint16_t等
避免提到的问题的一个方法是简单地做:
unsigned int flags = 0; flags = ~flags;
便携式和重点。
我不确定在C ++中使用unsigned int标志是一个好主意。 那么bitset之类的呢?
std::numeric_limit<unsigned int>::max()
更好,因为0xffffffff
假定unsigned int是一个32位整数。
unsigned int flags = -1; // all bits are true
“这是一个很好的,便携的方式来完成这个吗?”
便携式? 是的 。
好? 可疑的 ,这个线程显示的所有困惑都certificate了这一点。 要清楚你的程序员能够理解代码而不会混淆,这应该是我们衡量好代码的一个维度。
另外,这种方法很容易出现编译器警告 。 为了避免危害编译器,你需要明确的强制转换。 例如,
unsigned int flags = static_cast<unsigned int>(-1);
明确的转换要求您注意目标types。 如果你注意目标types,那么你自然会避免其他方法的陷阱。
我的build议是注意目标types,并确保没有隐式转换。 例如:
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转换为任何无符号types由标准保证为全1。 使用~0U
通常是不好的,因为0
types是unsigned int
并且不会填充更大的无符号types的所有位,除非明确地写入类似~0ULL
东西。 在理智的系统中,〜0应该与-1
相同,但是由于标准允许补码和符号/幅度表示,所以严格来说这不是可移植的。
当然,如果你知道你只需要32位就可以写出0xffffffff
,但是-1的好处是它可以在任何情况下工作,即使你不知道这个types的大小,例如在多个types,或者如果types的大小因实现而不同。 如果确实知道types,则另一种获得全部内容的安全方法是限制macrosUINT_MAX
, ULONG_MAX
, ULLONG_MAX
等。
我个人总是用-1。 它总是有效的,你不必考虑它。
是。 正如其他答案中提到的, -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;
只要你包含#include <limits.h>
,你就可以使用
unsigned int flags = UINT_MAX;
如果你想要一个很长的价值,你可以使用
unsigned long flags = ULONG_MAX;
这些值保证将结果集的所有值都设置为1,而不pipe签名整数如何实现。
我不会做-1的事情。 这是相当不直观的(至less对我来说)。 将签名数据分配给一个无符号variables似乎是违反了事物的自然顺序。
在你的情况下,我总是使用0xFFFF
。 (使用正确数量的Fs作为当然的可变尺寸。)
[顺便说一下,我很less看到在现实世界代码中完成的-1技巧。]
另外,如果你真的关心可放大的单个位,开始使用固定宽度的uint8_t
, uint16_t
, uint32_t
types是个好主意。
在英特尔的IA-32处理器上写入0xFFFFFFFF到64位寄存器并得到预期的结果是可以的。 这是因为IA32e(IA32的64位扩展)仅支持32位立即数。 在64位指令中,32位立即数被符号扩展为64位。
以下是非法的:
mov rax, 0ffffffffffffffffh
以下是在RAX中放置64个1:
mov rax, 0ffffffffh
为了完整起见,下面在RAX(又名EAX)的下半部分放置32个1:
mov eax, 0ffffffffh
事实上,当我想将0xffffffff写入64位variables时,程序就会失败,取而代之的是0xffffffffffffffff。 在C这将是:
uint64_t x; x = UINT64_C(0xffffffff) printf("x is %"PRIx64"\n", x);
结果是:
x is 0xffffffffffffffff
我想这张贴作为评论,所有的答案,说0xFFFFFFFF假设32位,但有这么多人回答,我想我会添加它作为一个单独的答案。
请参阅litb的答案,对问题做出非常清晰的解释。
我的不同意是,严格来说,这两种情况都不能保证。 我并不知道任何体系结构并不代表所有位设置的'1位小于2的无符号值'的无符号值,但这是标准实际所说的(3.9.1 / 7加附注44):
整体types的表示应使用纯二进制数字系统来定义值。 [注释44:]使用二进制数字0和1的整数的位置表示,其中由连续位表示的值是加法的,以1开始,并且乘以2的连续整数幂,除了最高的位置。
这使得其中一个位的可能性是任何东西。
实际上:是的
理论上:不。
-1 = 0xFFFFFFFF(或者你的平台上int的大小)只有在二进制补码运算中才是正确的。 在实践中,它会起作用,但是那里有一些遗留的机器(IBM大型机等),在那里你有一个实际的符号位,而不是二进制补码表示。 你提出的〜0解决scheme应该到处工作。
尽pipe0xFFFF
(或0xFFFFFFFF
等)可能更容易阅读,但它可能破坏本来便携的代码的可移植性。 例如,考虑一个库例程来计算数据结构中有多less项具有特定的位(被调用者指定的确切位)。 程序可能完全不知道这些位代表什么,但是仍然需要“所有位设置”恒定。 在这种情况下,-1会比一个hex常数好得多,因为它可以处理任何位的大小。
另一种可能性是,如果typedef
值用于位掩码,则将使用〜(bitMaskType)0; 如果掩码碰巧只是一个16位的types,那么这个expression式只会设置16位(即使“int”会是32位),但是由于16位将是所有需要的,实际上在types转换中使用了适当的types。
顺便说一下,如果hex常量太大而不能适应int
,但是会适合一个unsigned int
, longvar &= ~[hex_constant]
forms的expression式会有一个令人讨厌的问题。 如果int
是16位,那么longvar &= ~0x4000;
或longvar &= ~0x10000
; 将清除longvar
,但longvar &= ~0x8000;
将清除位15以及所有位。 符合int
值会将补码运算符应用于int
types,但结果将被标记为long
,并设置高位。 对于unsigned int
来说太大的值会将complement运算符应用于inputlong
。 然而,在这些大小之间的值将应用补码运算符来键入unsigned int
,然后将其转换为无符号扩展的long
unsigned int
。
正如其他人所提到的,-1是创build一个整数的正确方法,它将转换为所有位设置为1的无符号types。但是,C ++中最重要的是使用正确的types。 因此,你的问题的正确答案(包括你问的问题的答案)是这样的:
std::bitset<32> const flags(-1);
这将始终包含您需要的确切数量的位。 它构造了一个std::bitset
,其所有位都设置为1,这与其他答案中提到的相同。
这当然是安全的,因为-1总是有所有可用的位,但是我更喜欢〜0。 -1对于一个unsigned int
没有多大意义。 0xFF
…不好,因为它取决于types的宽度。
我说:
int x; memset(&x, 0xFF, sizeof(int));
这总是会给你想要的结果。
利用将所有位分配给无符号types的事实相当于为给定types取最大可能值,
并将问题的范围扩展到所有无符号整数types:
指定-1对于 C和C ++都适用于任何无符号整数types (unsigned int,uint8_t,uint16_t等)。
作为替代scheme,对于C ++,您可以:
- 包括
<limits>
并使用std::numeric_limits< your_type >::max()
- 编写一个自定义模板函数 (这也将允许一些理智检查,即如果目标types是一个真正的无符号types)
目的可以增加更多的清晰度,因为分配-1
总是需要一些解释性的评论。
是的,所显示的表示是非常正确的,就好像我们这样做了,反过来,你将需要一个操作符来反转所有的位,但是在这种情况下,如果我们考虑机器中整数的大小,这个逻辑非常简单
例如在大多数机器中,一个整数是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
因此它对于无符号整数是完全有效的
如有任何不一致的情况报告