按位运算符和整数提升会发生什么?

时间:2015-05-27 05:34:20

标签: c++ bitwise-operators unsigned-integer integer-promotion signed-integer

我有一个简单的程序。 请注意,我使用的是无符号固定宽度整数1个字节。

#include <cstdint>
#include <iostream>
#include <limits>

int main()
{
    uint8_t x = 12;
    std::cout << (x << 1) << '\n';
    std::cout << ~x;

    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cin.get();

    return 0;
}

我的输出如下。

24
-13

我测试了更大的数字,运算符<<总是给我正数,而运算符~总是给我负数。然后我使用了sizeof()并找到了......

  

当我使用左移位运算符(<<)时,我收到一个无符号的4字节整数。

     

当我使用按位非运算符(~)时,我收到一个带符号的4字节整数。

似乎按位非运算符(~)执行有符号整数提升,就像算术运算符一样。但是,左移运算符(<<)似乎会提升为无符号积分。

我觉得有必要知道编译器什么时候改变我背后的东西。如果我在分析中是正确的,那么所有按位运算符都会提升为4字节整数吗?为什么一些签名和一些未签名?我很困惑!

编辑:我总是得到肯定或总是得到负值的假设是错误的。但由于错误,我理解真正发生的事情,这要归功于下面的重要答案。

4 个答案:

答案 0 :(得分:8)

[expr.unary.op]

  

~的操作数应具有整数或无范围的枚举类型;该   结果是其操作数的补码。 整体促销活动   进行。

[expr.shift]

  

班次运营商<<>>从左到右分组。 [...]操作数应为整数或无范围的枚举类型,并且执行整体促销

uint8_t(通常会在幕后unsigned_char)的整体推广是什么?

[conv.prom]

  

boolchar16_tchar32_t以外的整数类型的prvalue,或者   wchar_t,其整数转换等级(4.13)小于等级   如果int可以代表所有int,则int可以转换为unsigned int类型的prvalue   源类型的值;否则,源prvalue可以   转换为int类型的prvalue。

所以uint8_t,因为int的所有值都可以由int(12) << 1表示。

什么是int(24)~int(12)

什么是int(-13)? {{1}}。

答案 1 :(得分:5)

出于性能原因,C和C ++语言认为int是最自然的&#34;整数类型,而不是&#34;较小的#34;比int被认为是&#34;存储&#34;类型。

在表达式中使用存储类型时,它会自动转换为intunsigned int。例如:

// Assume a char is 8 bit
unsigned char x = 255;
unsigned char one = 1;

int y = x + one; // result will be 256 (too large for a byte!)
++x;             // x is now 0

发生的事情是第一个表达式中的xone已被隐式转换为整数,已计算加法并将结果存储回整数。换句话说,没有使用两个无符号字符进行计算。

同样,如果表达式中有float值,编译器要做的第一件事就是将其提升为double(换句话说,float是存储类型而double 1}}是浮点数的自然大小)。这就是为什么如果你使用printf来打印浮动广告,你不需要在格式字符串中说%lf并且%f就足够了(%lfscanf需要,因为该功能存储结果,而float可能小于double

C ++使问题变得复杂,因为在将参数传递给函数时,您可以区分int和更小的类型。因此,在每个表达式中执行转换并不总是如此...例如,您可以拥有:

void foo(unsigned char x);
void foo(int x);

,其中

unsigned char x = 255, one = 1;
foo(x);       // Calls foo(unsigned char), no promotion
foo(x + one); // Calls foo(int), promotion of both x and one to int

答案 2 :(得分:4)

  

我测试了更大的数字和运算符&lt;&lt;总是给我积极的   数字,而操作员〜总是给我负数。然后我   使用了sizeof()并找到了......

错了,测试一下:

uint8_t v = 1;
for (int i=0; i<32; i++) cout << (v<<i) << endl;

给出:

1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
-2147483648

uint8_t是一个8位长的无符号整数类型,它可以表示[0,255]范围内的值,因为它包含在int范围内的范围被提升为{{1} (不是int)。促销unsigned int优先于促销int

答案 3 :(得分:3)

查看two's complement以及计算机如何存储负整数 试试这个

#include <cstdint>
#include <iostream>
#include <limits>
int main()
{
uint8_t x = 1;
int shiftby=0;
shiftby=8*sizeof(int)-1;
std::cout << (x << shiftby) << '\n'; // or std::cout << (x << 31) << '\n';

std::cout << ~x;

std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
return 0;
}

输出为-2147483648

通常,如果有符号数的第一位为1,则认为是负数。当你拿大量并转移它。如果你移动它使第一位为1则为负

**编辑**
好吧,我可以想到为什么移位运算符会使用unsigned int。考虑右移操作>>如果你右移-12你会得到 122 而不是-6。这是因为它在开头添加零而不考虑符号