我已经看到这种模式在C&C ++中使用了很多。
unsigned int flags = -1; // all bits are true
这是一个很好的便携式方法吗?或者更好地使用0xffffffff
或~0
?
我建议你完全按照你所展示的那样去做,因为它是最直接的。初始化为-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
初始化为零。
你应该理解的是,所有这些都与价值观有关 - 而不是比特。变量用值初始化。如果在初始值设定项中修改了用于初始化的变量的位,则将根据这些位生成该值。将a
初始化为最高可能值所需的值是-1
或UINT_MAX
。第二个将取决于a
的类型 - 你需要使用ULONG_MAX
作为unsigned long
。但是,第一个不依赖于它的类型,这是获得最高价值的好方法。
我们不是在谈论-1
是否具有所有位(它并不总是如此)。而且我们不是在谈论~0
是否所有位都是(它当然有)。
但我们所说的是初始化的flags
变量的结果是什么。而对于它,只有-1
将适用于所有类型和机器。
我不会做-1的事情。这是非直观的(至少对我而言)。将签名数据分配给无符号变量似乎违反了事物的自然顺序。
在你的情况下,我总是使用0xFFFF
。 (当然,可以使用正确数量的Fs作为可变大小。)
[顺便说一句,我很少看到真实世界代码中的-1技巧。]
另外,如果你真的关心可用的各个位,那么最好开始使用固定宽度的qazxsw poi,qazxsw poi,qazxsw poi类型。
在Intel的IA-32处理器上,可以将0xFFFFFFFF写入64位寄存器并获得预期的结果。这是因为IA32e(IA32的64位扩展)仅支持32位立即数。在64位指令中,32位立即符号扩展为64位。
以下是非法的:
uint8_t
以下是RAX中的64个1:
uint16_t
为了完整起见,以下将32个1放在RAX(又名EAX)的下半部分:
uint32_t
事实上,当我想将0xffffffff写入64位变量并且我得到一个0xffffffffffffffff时,我的程序失败了。在C中,这将是:
mov rax, 0ffffffffffffffffh
结果是:
mov rax, 0ffffffffh
我想发布这个作为对所有答案的评论,说0xFFFFFFFF假设32位,但是很多人回答它我想我会把它作为一个单独的答案添加。
请参阅litb的答案,以便对问题进行非常明确的解释。
我的不同意见是,严格来说,两种情况都无法保证。我不知道任何体系结构都没有表示无符号值,比所有位设置的“少于2位的功率”,但这是标准实际所说的(3.9.1 / 7加注44):
整数类型的表示应使用纯二进制计算系统定义值。 [注44:]使用二进制数字0和1的整数的位置表示,其中由连续位表示的值是加法的,以1开始,并且乘以2的连续积分幂,除了可能的位最高的位置。
这留下了其中一个位可能是任何东西的可能性。
实际上:是的
理论上:没有。
-1 = 0xFFFFFFFF(或者你的平台上的int的大小)只有二进制补码算法才有效。在实践中,它会起作用,但是那里有遗留机器(IBM大型机等),你有一个实际的符号位而不是二进制补码表示。你提议的〜0解决方案应该适用于所有地
尽管mov eax, 0ffffffffh
(或uint64_t x;
x = UINT64_C(0xffffffff)
printf("x is %"PRIx64"\n", x);
等)可能更容易阅读,但它可能会破坏代码的可移植性,否则这些代码将是可移植的。例如,考虑一个库例程来计算数据结构中有多少项设置了某些位(确切的位由调用者指定)。例程可能完全不知道比特代表什么,但仍需要“所有比特设置”不变。在这种情况下,-1将比十六进制常量好得多,因为它可以用于任何位大小。
如果将x is 0xffffffffffffffff
值用于位掩码,则另一种可能性是使用〜(bitMaskType)0;如果位掩码恰好只是一个16位类型,那么该表达式只会设置16位(即使'int'否则将是32位),但由于16位将是所有需要的,所以一切都应该没问题。实际上在类型转换中使用了适当的类型。
顺便说一句,如果十六进制常数太大而不适合0xFFFF
,那么0xFFFFFFFF
形式的表达式会有一个令人讨厌的问题,但它会适合typedef
。如果longvar &= ~[hex_constant]
是16位,那么int
或unsigned int
;将清除一点int
,但longvar &= ~0x4000;
将清除第15位和所有位以上。适合longvar &= ~0x10000
的值将补码运算符应用于类型longvar
,但结果将符号扩展到longvar &= ~0x8000;
,设置高位。对于int
而言太大的值将使用补充运算符应用于int
类型。但是,这些大小之间的值将应用补码运算符键入long
,然后将其转换为类型unsigned int
而不带符号扩展名。
正如其他人所提到的,-1是创建一个整数的正确方法,该整数将转换为无符号类型,所有位都设置为1.但是,C ++中最重要的是使用正确的类型。因此,您问题的正确答案(包括您提出的问题的答案)是:
long
这将始终包含您需要的确切位数。由于其他答案中提到的相同原因,它构造了一个unsigned int
,所有位都设置为1。
它肯定是安全的,因为-1将始终设置所有可用位,但我更喜欢~0。 -1只是对long
没有多大意义。 std::bitset<32> const flags(-1);
...不好,因为它取决于类型的宽度。
我说:
std::bitset
这将始终为您提供所需的结果。
利用将无符号类型的所有位分配给1的事实等同于获取给定类型的最大可能值, 并将问题的范围扩展到所有无符号整数类型:
C和C ++的unsigned int
(unsigned int,uint8_t,uint16_t等)。
作为替代方案,对于C ++,您可以:
0xFF
并使用int x;
memset(&x, 0xFF, sizeof(int));
目的可以更加清晰,因为分配<limits>
总是需要一些解释性的评论。
一种使意义更明显但又避免重复类型的方法:
std::numeric_limits< your_type >::max()
unsigned int flags = -1;
是便携式的。unsigned int flags = ~0;
不可移植,因为它依赖于二进制补码表示。unsigned int flags = 0xffffffff;
不可移植,因为它采用32位整数。如果要以C标准保证的方式设置所有位,请使用第一个。
是的,显示的表示是非常正确的,好像我们这样做,反过来你需要一个操作符来反转所有的位,但在这种情况下,如果我们考虑机器中整数的大小,逻辑是非常简单的
例如,在大多数机器中,整数是2个字节= 16位最大值它可以容纳2 ^ 16-1 = 65535 2 ^ 16 = 65536
0%65536 = 0 -1%65536 = 65535,它们应对1111 ............. 1并且所有位都设置为1(如果我们考虑残差类mod 65536)因此它很多直截了当。
我猜
不,如果你认为这个概念对于无符号的整数是完全用餐的,它实际上是有效的
只需检查以下程序片段
int main(){
custom templated function
}
答案为b = 4294967295,即4字节整数为-1%2 ^ 32
因此它对无符号整数完全有效
如有任何差异,请发送报告
坦率地说,我认为所有的fff都更具可读性。至于它是反模式的评论,如果你真的关心所有的位都被设置/清除,我会争辩说你可能处于这样一种情况,你无论如何都要关心变量的大小,这会要求像boost这样的东西:: uint16_t等
避免上述问题的一种方法是简单地做:
unsigned int flags = 0;
flags = ~flags;
便携式和重点。
我不确定在C ++中首先使用unsigned int for flags是一个好主意。比特怎么样?
std::numeric_limit<unsigned int>::max()
更好,因为0xffffffff
假设unsigned int是一个32位整数。
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转换为任何无符号类型以产生全1。 ~0U
的使用通常很糟糕,因为0
具有类型unsigned int
并且不会填充更大的无符号类型的所有位,除非你明确写出类似~0ULL
的东西。在理智的系统上,~0
应该与-1
相同,但由于该标准允许补码和符号/幅度表示,严格来说它不是便携式的。
当然,如果你知道你需要正好32位,那么写出0xffffffff
总是可以的,但是-1的优势在于,即使你不知道类型的大小,它也能在任何环境中工作,例如可以处理多个宏的宏类型,或者类型的大小因实现而异。如果你知道这种类型,另一种安全的方法来获得所有 - 是限制宏UINT_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。