C语言 a|=(1〈〈31)导致意外值

hc8w905p  于 2023-04-11  发布在  其他
关注(0)|答案(3)|浏览(425)

我做了一个可复制的代码如下/

#include <stdio.h>

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

    a |= (1 << 31);

    printf("a: 0x%lx\n", a);

}

我希望'a'应该是0x 000000080000000。
但其值为
a:0xffffffff80000000。
为什么高32位以编程方式填充1,如果我想让'a'作为单个1和所有0值,我该怎么做?

sbdsn5lh

sbdsn5lh1#

1是一个signed int。当你把它左移31位时,你会得到一个负数INT_MIN(假设32位整数和2是合格的)。然后当你把它赋值给long int(假设64位)时,它被符号扩展为具有相同的负值。
它是C标准未定义的行为,但大多数现代计算机都有算术移位指令,并使用二进制补码编码。
你需要使用一个无符号数:

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

    a |= (1U << 31);

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

}

https://godbolt.org/z/G95GYffPM
或者你需要在二进制或之前将其转换为unsigned int

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

    a |= (unsigned int)(1 << 31);

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

}

https://godbolt.org/z/bq3j4dWsc

13z8s7eq

13z8s7eq2#

来自C标准(6.5.7按位移位运算符)
3对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。
因此,此语句中使用的表达式( a << 31 )的类型

a |= (1 << 31);

是有符号整数类型int
进一步(相同的C标准部分)
4 E1〈〈E2的结果是E1左移的E2比特位置;如果E1是无符号类型,则结果的值是E1 × 2 E2,比结果类型中可表示的最大值大1。如果E1是有符号类型和非负值,并且E1 × 2 E2在结果类型中可表示,则这是结果值;否则,行为未定义。
假设一个int类型的对象包含32位,并且最高有效位是符号位,那么表达式(1 << 31 )的值在int类型的对象中是不可表示的,因此表达式调用未定义的行为。
您至少需要使用无符号整数类型unsigned int,例如

a |= (1u << 31);

无符号long int

a |= (1lu << 31);

请注意,结果取决于类型long int的大小。它可以等于4作为类型int的大小,也可以等于8作为类型long long int的大小。
而不是在调用printf的格式字符串中手动写入0x

printf("a: 0x%lx\n", a);

您可以使用标记#,如

printf("a: %0#16lx\n", a);
j2qf4p5b

j2qf4p5b3#

变更

a |= (1 << 31);

a |= (1UL << 31);

1是一个int常数,它等于signed int

相关问题