所有这些功能都可以在我的机器上提供预期的结果。他们都在其他平台上工作吗?
更具体地说,如果x在1的补码机器上具有位表示0xffffffff或在有符号的幅度机器上具有0x80000000,那么标准对于(无符号)x的表示有什么说明?
另外,我认为v2,v2a,v3,v4中的(unsigned)强制转换是多余的。这是对的吗?
假设sizeof(int)= 4且CHAR_BIT = 8
int logicalrightshift_v1 (int x, int n) {
return (unsigned)x >> n;
}
int logicalrightshift_v2 (int x, int n) {
int msb = 0x4000000 << 1;
return ((x & 0x7fffffff) >> n) | (x & msb ? (unsigned)0x80000000 >> n : 0);
}
int logicalrightshift_v2a (int x, int n) {
return ((x & 0x7fffffff) >> n) | (x & (unsigned)0x80000000 ? (unsigned)0x80000000 >> n : 0);
}
int logicalrightshift_v3 (int x, int n) {
return ((x & 0x7fffffff) >> n) | (x < 0 ? (unsigned)0x80000000 >> n : 0);
}
int logicalrightshift_v4 (int x, int n) {
return ((x & 0x7fffffff) >> n) | (((unsigned)x & 0x80000000) >> n);
}
int logicalrightshift_v5 (int x, int n) {
unsigned y;
*(int *)&y = x;
y >>= n;
*(unsigned *)&x = y;
return x;
}
int logicalrightshift_v6 (int x, int n) {
unsigned y;
memcpy (&y, &x, sizeof (x));
y >>= n;
memcpy (&x, &y, sizeof (x));
return x;
}
答案 0 :(得分:8)
如果x在1的位上具有位表示0xffffffff 补充机器或0x80000000在签名量级机器上 标准是否表示(无符号)x?
的表示
转换为unsigned
是根据值指定的,而非表示形式。如果您将-1
转换为unsigned
,则始终获取UINT_MAX
(因此,如果您的unsigned
为32位,则始终获得4294967295
})。无论您的实现使用的有符号数字的表示如何,都会发生这种情况。
同样,如果您将-0
转换为unsigned
,那么您始终获取0
。 -0
在数值上等于0.
请注意,不需要补码或符号幅度实现来支持负零;如果没有,则访问此类表示会导致程序具有未定义的行为。
逐个完成您的功能:
int logicalrightshift_v1(int x, int n)
{
return (unsigned)x >> n;
}
x
的负值此函数的结果取决于UINT_MAX
,如果(unsigned)x >> n
不在int
范围内,则会进一步实现定义。例如,logicalrightshift_v1(-1, 1)
将返回值UINT_MAX / 2
,无论机器使用哪种代表签名数字。
int logicalrightshift_v2(int x, int n)
{
int msb = 0x4000000 << 1;
return ((x & 0x7fffffff) >> n) | (x & msb ? (unsigned)0x80000000 >> n : 0);
}
几乎所有关于此的内容都可以是实现定义的。假设您试图在msb
中创建一个值,符号位为1,值位为零,则不能通过使用移位来移植 - 您可以使用~INT_MAX
,但这是允许在不允许负零的符号幅度机器上具有未定义的行为,并且允许在两个补码机器上给出实现定义的结果。
0x7fffffff
和0x80000000
的类型取决于各种类型的范围,这将影响此表达式中其他值的提升方式。
int logicalrightshift_v2a(int x, int n)
{
return ((x & 0x7fffffff) >> n) | (x & (unsigned)0x80000000 ? (unsigned)0x80000000 >> n : 0);
}
如果您创建的unsigned
值不在int
范围内(例如,给定32位int
,值&gt; 0x7fffffff
)则隐含return语句中的转换生成实现定义的值。这同样适用于v3和v4。
int logicalrightshift_v5(int x, int n)
{
unsigned y;
*(int *)&y = x;
y >>= n;
*(unsigned *)&x = y;
return x;
}
这仍然是实现定义,因为未指定int
表示中的符号位是否对应于unsigned
表示中的值位或填充位。如果它对应于填充位,则它可能是陷阱表示,在这种情况下,行为是未定义的。
int logicalrightshift_v6(int x, int n)
{
unsigned y;
memcpy (&y, &x, sizeof (x));
y >>= n;
memcpy (&x, &y, sizeof (x));
return x;
}
适用于v5的相同评论适用于此。
另外,我认为v2,v2a,v3,v4中的(unsigned)强制转换是多余的。是 这是正确的吗?
这取决于。作为十六进制常量,如果该值在0x80000000
范围内,则int
将具有类型int
;如果该值在unsigned
范围内,则为unsigned
;如果该值在long
范围内,则为long
;否则unsigned long
(因为该值在unsigned long
)的最小允许范围内。
如果您希望确保它具有无符号类型,则使用U
将常量后缀为0x80000000U
。
<强>要点:强>
将大于INT_MAX
的数字转换为int
会得到实现定义的结果(或者实际上,允许引发实现定义的信号)。
通过重复添加或减去unsigned
来完成将超出范围的数字转换为UINT_MAX + 1
,这意味着它取决于数学值,不是代表。
检查否定的int
表示为unsigned
是不可移植的(正int
表示也可以)。
通过使用按位运算符生成负零并尝试使用结果值是不可移植的。
如果您想要“逻辑转换”,那么您应该在任何地方使用无符号类型。签名类型用于处理算法,其中值是重要的,而不是表示。
答案 1 :(得分:2)
如果您遵循该标准,那么在所有平台上都不能保证这些标准不相同。
在v5中,违反了严格别名,这是未定义的行为。
在v2 - v4中,您已经签署了右移,这是实现定义的。 (有关详细信息,请参阅评论)
在v1中,您已签名为无符号演员,这是在数字超出范围时定义的实现。
编辑:
v6 可能实际上有以下假设:
unsigned
和int
的大小完全相同(包括字节和位,并且密集包装)。unsigned
的结束符合int
的结尾。