a |= (1 << 31) 导致意外数值。

huangapple go评论73阅读模式
英文:

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 &lt;stdio.h&gt;

int main(void)
{
    long int a = 0x0;

    a |= (1 &lt;&lt; 31);

    printf(&quot;a: 0x%lx\n&quot;, 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 &lt;&lt; 31);

    printf(&quot;a: 0x%016lx\n&quot;, 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 &lt;&lt; 31);

    printf(&quot;a: 0x%016lx\n&quot;, a);

}

https://godbolt.org/z/bq3j4dWsc

答案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 &lt;&lt; 31 ) used in this statement

a |= (1 &lt;&lt; 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 &lt;&lt; 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 &lt;&lt; 31);

or unsigned long int

a |= (1lu &lt;&lt; 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(&quot;a: 0x%lx\n&quot;, a);

you could use flag # like

printf(&quot;a: %0#16lx\n&quot;, a);

答案3

得分: 1

将以下内容翻译为中文:

a |= (1 << 31);

更改为

a |= (1UL << 31);

1 是一个 int 常数,默认情况下是有符号的。

英文:

Change

a |= (1 &lt;&lt; 31);

to

a |= (1UL &lt;&lt; 31);

1 is an int constant, which is signed int by deafult.

huangapple
  • 本文由 发表于 2023年4月10日 19:13:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/75976580.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定