У меня есть простая программа. Обратите внимание, что я использую целое число без знака с фиксированной шириной 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-байтовое целое число? И почему некоторые подписаны, а некоторые без подписи? Я весьма озадачен!
Редактировать: Мое предположение о том, чтобы всегда получать положительные или всегда отрицательные значения, было неверным. Но из-за неправильного ответа я понимаю, что на самом деле происходит, благодаря замечательным ответам ниже.
[Expr.shift]Операнд
~
должен иметь целочисленный или незаданный тип перечисления;
результат — дополнение его операнда. Интегральные акции
выполнила.
Операторы сдвига
<<
а также>>
группа слева направо. […] Операнды должны быть целочисленного или незаданного типа перечисления и выполняются интегральные рекламные акции.
Что является неотъемлемым продвижением uint8_t
(который обычно будет unsigned_char
за кулисами)?
Значение типа integer, отличного от
bool
,char16_t
,char32_t
, или же
wchar_t
чей ранг целочисленного преобразования (4.13) меньше, чем ранг
int
может быть преобразовано в тип значенияint
еслиint
может представлять все
значения типа источника; в противном случае исходное значение может быть
преобразуется в значение типаunsigned int
,
Так int
потому что все значения uint8_t
может быть представлен int
,
Что такое int(12) << 1
? int(24)
,
Что такое ~int(12)
? int(-13)
,
Из соображений производительности язык C и C ++ учитывают int
быть «наиболее естественным» целочисленным типом и вместо этого типа, которые «меньше», чем int
считаются своего рода «хранилищем» типа.
Когда вы используете тип хранения в выражении, он автоматически преобразуется в int
или к unsigned 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
Однако, потому что эта функция сохраняет результат и 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
Заглянуть в дополнение двух и как компьютер хранит отрицательные целые числа.
Попробуй это
#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, он будет отрицательным
** РЕДАКТИРОВАТЬ **
Ну, я могу придумать причину, по которой операторы shift будут использовать unsigned int. Рассмотрим операцию правого сдвига >>
если вы сдвинете вправо -12, вы получите 122 вместо -6. Это потому, что он добавляет ноль в начале без учета знака
Я проверил большие цифры и оператор << всегда дает мне позитив
числа, в то время как оператор ~ всегда дает мне отрицательные числа. После, я
использовал 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
он повышен до int
(не unsigned int
). Продвижение в int
имеет приоритет над продвижением unsigned
,