英文:
a |= (1 << 31) results in unexepected value
问题
#include <stdio.h>
int main(void)
{
long int a = 0x0;
a |= (1 << 31);
printf("a: 0x%lx\n", a);
}
我做了一个如下的可重现代码。
期望 'a' 应该是 0x0000000080000000。
但是值显示为
a: 0xffffffff80000000。
为什么程序上会将前32位填充为1,如果我想将 'a' 设为单一的1和全部为0的值,我该怎么做?
英文:
I made a reproducible code as below/
#include <stdio.h>
int main(void)
{
long int a = 0x0;
a |= (1 << 31);
printf("a: 0x%lx\n", a);
}
I expect 'a' should be 0x0000000080000000.
but the value is given as
a: 0xffffffff80000000.
Why are upper 32 bits filled with 1 programmatically, and if I want to make 'a' as single one and all zero value, what can I do?
答案1
得分: 4
1
是一个有符号整数 (signed int
)。当你将它左移 31 位时,你会得到一个负数,其值为 INT_MIN
(假设使用 32 位整数和补码表示)。然后,当你将它赋给 long int
(假设使用 64 位),它会被符号扩展以具有相同的负值。
这在 C 标准中是未定义的行为,但大多数现代计算机都具有算术右移指令,并使用补码编码。
你需要使用一个无符号数来代替:
int main(void)
{
long int a = 0x0;
a |= (1U << 31);
printf("a: 0x%016lx\n", a);
}
或者在进行按位或之前将其强制转换为 unsigned int
:
int main(void)
{
long int a = 0x0;
a |= (unsigned int)(1 << 31);
printf("a: 0x%016lx\n", a);
}
英文:
1
is a signed int
. When you shift it 31 bits left you get a negative number of INT_MIN
(assuming 32 bits integer and two's competent). Then when you assign it to the long int
(assuming 64 bits) it is being signed extended to have the same negative value.
It is undefined behaviour by the C standard but most modern computers have arithmetic shift instructions and use two's complement coding.
You need to use an unsigned number instead:
int main(void)
{
long int a = 0x0;
a |= (1U << 31);
printf("a: 0x%016lx\n", a);
}
https://godbolt.org/z/G95GYffPM
or you need to cast it to unsigned int
before binary or.
int main(void)
{
long int a = 0x0;
a |= (unsigned int)(1 << 31);
printf("a: 0x%016lx\n", a);
}
答案2
得分: 2
从C标准(6.5.7 位移运算符)中可以得出以下信息:
- 整数提升将分别在操作数上执行。结果的类型是左操作数提升后的类型。
- 因此,在这个语句中使用的表达式
( a << 31 )
的类型是有符号整数类型int
。
此外,C标准的同一部分还提到:
- E1 << E2 的结果是将 E1 左移 E2 位,空出的位将填充为零。如果 E1 具有无符号类型,则结果的值是 E1 * 2^E2,对结果类型的最大可表示值加一取模。如果 E1 具有有符号类型且非负值,并且 E1 * 2^E2 可以在结果类型中表示,那么这将是结果值;否则,行为未定义。
鉴于类型 int
包含 32
位,最高位是符号位,因此表达式 (1 << 31 )
的值不能在类型 int
的对象中表示,因此该表达式引发了未定义行为。
您可以使用无符号整数类型 unsigned int
,例如:
a |= (1u << 31);
或无符号长整数类型 unsigned long int
,例如:
a |= (1lu << 31);
需要注意的是,结果取决于类型 long int
的大小,它可以等于 4
(与类型 int
相同)或等于 8
(与类型 long long int
相同)。
另外,您可以在 printf
的格式字符串中使用 #
标志,而不是手动写入 0x
,例如:
printf("a: %#16lx\n", a);
英文:
From the C Standard (6.5.7 Bitwise shift operators)
> 3 The integer promotions are performed on each of the operands. The
> type of the result is that of the promoted left operand.
So the type of the expression ( a << 31 )
used in this statement
a |= (1 << 31);
is the signed integer type int
.
Further (the same C Standard section)
> 4 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated
> bits are filled with zeros. If E1 has an unsigned type, the value of
> the result is E1 × 2E2, reduced modulo one more than the maximum value
> representable in the result type. If E1 has a signed type and
> nonnegative value, and E1 × 2E2 is representable in the result type,
> then that is the resulting value; otherwise, the behavior is
> undefined.
Provided that an object of the type int
contains 32
bits and the most-significant bit is the sign bit then the value of the expression (1 << 31 )
is not representable in an object of the type int
and hence the expression invokes undefined behavior.
You need to use at least unsigned integer type unsigned int
as for example
a |= (1u << 31);
or unsigned long int
a |= (1lu << 31);
Pay attention to that the result depends on the size of the type long int
. It can be equal either to 4
as the size of the type int
or 8
as the type long long int
.
Instead of writing manually 0x
in the format string of the call of printf
printf("a: 0x%lx\n", a);
you could use flag #
like
printf("a: %0#16lx\n", a);
答案3
得分: 1
将以下内容翻译为中文:
将
a |= (1 << 31);
更改为
a |= (1UL << 31);
1
是一个 int
常数,默认情况下是有符号的。
英文:
Change
a |= (1 << 31);
to
a |= (1UL << 31);
1
is an int
constant, which is signed int
by deafult.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论