整型数的左移操作

4

我在IndiaBix.com上看到了以下问题。根据我的经验水平(刚学习C语言),上述代码的输出应该是0 (10000000 << 1 是00000000),但实际输出却是256。进一步分析后发现我们使用的是%d进行打印,它只支持4个字节,因此输出为256而不是0。

#include<stdio.h> 
    int main()
    {
        unsigned char i = 128;
        printf("%d \n", i << 1);
        return 0;
    }

现在考虑下面的例子。
#include<stdio.h>

int main()
{
    unsigned int i = 2147483648;(bit 31 = 1 and b0 to b30 are 0)
    printf("%d \n", i<<1);
    return 0;
}

当我将上述内容左移时,输出结果为0。由于%d支持int类型的值,因此输出结果应该是0,但是当我将%d更改为%ld时,输出结果仍然是0。由于%ld支持long int类型的值,因此输出结果不应该是0。为什么我会得到0作为输出结果。


4
改变printf的格式说明符并不会改变您实际进行计算的类型的大小。 unsigned int 通常是4个字节,但不能保证。对于您的类型使用错误的 printf说明符是非法的:未定义行为 可能会给您带来非常令人困惑的结果,包括崩溃,打印奇怪的东西,甚至看起来正常工作。 - BoBTFish
3
您可能需要了解整型提升,它会导致您的unsigned char变量在移位操作之前被提升为int - Some programmer dude
请注意格式说明符,http://trust-in-soft.com/printing-an-unsigned-char-with-x-or-u-is-not-absolutely-correct/ - Giorgi Moniava
5个回答

2
在第一种情况下,变量i被提升为int类型,它可以存储至少32767,然后进行位移计算。因此,结果变为256。
在第二种情况下,如果您的unsigned int是32位长,则会以unsigned int计算并环绕。因此,结果为0。
您必须将其转换为更大的类型才能得到所需的结果。
#include<stdio.h>
#include<inttypes.h>

int main()
{
    unsigned int i = 2147483648;//(bit 31 = 1 and b0 to b30 are 0)
    printf("%"PRIu64" \n", (uint64_t)i<<1);
    return 0;
}

2

这里重要的不是 %d 或者 %ld

可能是因为你机器上无符号整型的大小是 4 字节。

此外,你不能在 unsigned int 上使用 %ld,这是未定义的行为。


1
第一个问题是,2147483648(80000000h)可以放在您的32位无符号整数内,但不能放在使用%d指示符时printf期望的有符号int中。相反,请使用%u指示符。
当这个问题被解决后,请注意,如果无符号整数为32位,则0x80000000 << 1应该为0。
printf的格式指示符更改为%ld不会更改表达式的类型!如果要使用更大的类型,则需要同时更改格式指示符和表达式。
您正在被第一个char打印的行为所欺骗。之所以%d在那里起作用,是因为printf(像任何可变参数函数一样)在内部将所有参数提升为至少int的大小。因此,表达式被隐式提升为int,恰好与%d匹配。
尽管在my_char << n的情况下,<<运算符已经将两个操作数都提升为int类型,移位的结果始终具有可能被提升的左操作数的类型。

正如你所看到的,C语言中的各种隐式类型转换规则并不简单,因此很容易产生错误。这是该语言众多公认的缺陷之一。


0
根据C标准(6.5.7位移运算符):
3. 对每个操作数执行整数提升。结果的类型与提升后的左操作数相同。
以及(6.3.1.1布尔值、字符和整数):
2. 如果int可以表示原始类型的所有值(受限于位域的宽度),则将值转换为int;否则,将其转换为unsigned int。这些被称为整数提升。其他所有类型都不受整数提升影响。
这意味着unsigned char类型的操作数会被提升为int类型,因为int类型可以表示unsigned char类型的所有值。
所以在表达式中,
i << 1

操作数i被提升为类型int,并且您将获得(假设类型int有32位)

0x00000080 << 1

操作完成后,您将获得结果

0x00000100

对应于十进制的

256

以及这个语句

printf("%d \n", i << 1);

输出这个结果。

现在考虑这段代码片段。

unsigned int i = 2147483648;(bit 31 = 1 and b0 to b30 are 0)
printf("%d \n", i<<1);

这里的i可以表示为

0x80000000

整数提升是应用于类型具有比类型int更低的转换级别的。

因此,对变量i不会应用整数提升,因为它的级别不低于int的级别。

操作后你将得到

0x00000000

这意味着你会得到一个十进制数

0

以及这个语句

printf("%d \n", i<<1);

这段代码将正确输出零,因为无符号整数对象和有符号整数对象的表示方式相同。

即使您编写如下代码

printf( "%lld \n", ( long long int )( i<<1 ));

你将会得到相同的结果,因为无论何种情况下表达式 i << 1 的类型都是 unsigned int

然而,如果你写成

printf( "%lld \n", ( long long int )i << 1 );

如果操作数i将被转换为long long int类型,你将得到
0x0100000000

0

将 %d 更改为 %id 不会有效。将数据类型更改为无符号即可帮助您增加整数限制。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接