C++ 位运算符和整数提升是怎么回事?

C++ 位运算符和整数提升是怎么回事?,c++,bitwise-operators,unsigned-integer,integer-promotion,signed-integer,C++,Bitwise Operators,Unsigned Integer,Integer Promotion,Signed Integer,我有一个简单的程序请注意,我使用了一个大小为1字节的无符号固定宽度整数。 我测试了较大的数字和运算符查看计算机如何存储负整数。 试试这个 #包括 #包括 #包括 int main() { uint8_t x=1; int-shiftby=0; shiftby=8*sizeof(int)-1; 标准::cout[expr.unary.op] ~的操作数应具有整型或非作用域枚举类型 结果是一的补充 它的操作数。整数提升是 已执行。 [解释移位] 移位运算符从左到右分组。[…]操作数应为整数或无范围枚

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

我测试了较大的数字和运算符
查看计算机如何存储负整数。
试试这个

#包括
#包括
#包括
int main()
{
uint8_t x=1;
int-shiftby=0;
shiftby=8*sizeof(int)-1;
标准::cout[expr.unary.op]

~
的操作数应具有整型或非作用域枚举类型 结果是一的补充 它的操作数。整数提升是 已执行。

[解释移位]

移位运算符
从左到右分组。[…]操作数应为整数或无范围枚举类型,并执行整数提升

uint8\u t
的整体推广是什么(通常是
unsigned\u char
的幕后推广)

[conv.prom]

整数类型的PR值,而不是
bool
char16\u t
char32\u t
,或
wchar\u t
其整数转换秩(4.13)小于
int
可以转换为
int
类型的PR值,如果
int
可以表示所有 源类型的值;否则,源值可以为 已转换为类型为
无符号int
的PR值

所以
int
,因为
uint8\u t
的所有值都可以用
int
表示

什么是
int(12)

我测试了较大的数和运算符

,因为C和C++语言认为 int >代码>是“最自然”的整数类型,而“小于”代码代码的类型被认为是“存储”类型。

在表达式中使用存储类型时,它会自动隐式转换为
int
无符号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
发生的情况是,第一个表达式中的
x
one
已隐式转换为整数,加法已计算,结果已存储回整数。换句话说,计算未使用两个无符号字符

同样,如果表达式中有一个
float
值,编译器要做的第一件事就是将其升级为
double
(换句话说
float
是一种存储类型,
double
是浮点数的自然大小)。这就是为什么如果使用
printf
打印浮动,则无需在格式字符串中输入
%lf
,并且
%f
就足够了(
%lf
对于
scanf
是必需的,但是因为该函数存储一个结果,并且
浮动
可以小于
双精度

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

void foo(unsigned char x);
void foo(int x);
在哪里


我不相信这完全回答了我的问题,但让我按照你的说法,也许我会弄明白。当你左移31次,值溢出。从这一点来看,左移运算符进行有符号整数提升,而不是无符号整数提升。如果这是真的,那么这意味着编译器c检查二进制数最左边的数字以确定其符号。必须是这样!当操作数比
int
更窄时,编译器必须始终使用位运算符进行整数提升。等等,什么?我不知道您使用哪个编译器来测试它,但x86_64上的GCC发出带符号移位的SAR指令,这将保留符号。因此,(-12>>1)==-6.如何让流以数字而不是字符的形式输出
uint8\u t
?您确定编译器没有将该类型别名为
int
?@AntonSamsonov在下面的回答中解释了这一点,因为在按位操作发生后,会发生积分提升。换句话说,数据类型从
uint8\u t
提升为
int
。这也是我在使用按位not运算符(
~
)后得到所有负数的原因这是因为大多数二进制数从左边开始都有尾随的0,当它们被翻转时,最左边的数字很可能是1,使数字为负数。如果存储该值的内存大小为4字节,给了我2^32个可能的值,并且如果我选择的值远小于此范围,则情况尤其如此。看起来像是y你从一本深入的C++书籍或手册中找到了编译器信息。如果它是,它是什么?如果我在编译器操作上遇到问题,我想把它当作一个资源。@ WaveRealDisto“从左边拖尾”被称为“领先”。C++被称为C++标准(不是推荐资源)。任何一个体面的C++介绍本应该涵盖大部分,例如我喜欢你最后一句关于如何在传递函数参数时不执行隐式转换的评论。这是一些有用的信息,谢谢。什么是“自然”的?好,编译器可以做任何它需要/想要计算的结果。(以最快的方式,或者考虑其他因素),但它应该保留(最大的)操作数类型。只有程序员应该扩展表达式类型-使用显式强制转换;任何其他行为都是违反直觉的。还是更“自然”从你的观点来看,在所有表达式周围写显式的转换,将它们放回到原来的域中?“同样,如果你在表达式中有一个浮点值,编译器会做的第一件事就是把它提升到一个双”——这在C++中是不正确的。
#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;
}
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
// 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
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