5.17 Примечания (Операторы)

1. Арифметические операторы

 + - * / %

нота:

  1. %Оба операнда оператора должны быть целыми числами.
  2. Остальные несколько операторов можно использовать для целых чисел и чисел с плавающей запятой.
  3. Для /операторов, если оба числа являются целыми числами, выполнить целочисленное деление, если одно из чисел с плавающей запятой, выполнить деление с плавающей запятой.

2. Оператор сдвига

  1. Логический сдвиг: двоичный бит сдвигается влево и вправо, а два конца дополняются 0, поэтому нет необходимости заботиться о знаковом бите.
  2. Арифметический сдвиг: двоичный бит сдвигается влево и вправо, а два конца дополняются 0, но знаковый бит должен оставаться неизменным.

Введите 4 инструкции в сборку:
SHL: Сдвиг влево логически
SHR: Сдвиг вправо логически
SAL: Арифметический сдвиг влево
SAR: Арифметический сдвиг вправо

int main()
{
	char i = 128;
	i = i >> 3;
	printf("%d\n", i);
	return 0;
}

Использует ли приведенный выше блок кода логический сдвиг вправо или арифметический сдвиг вправо?

1. Сдвиг влево целого положительного числа со знаком

int main()
{
	int i = 1;
	i = i << 3;
	return 0;
}

Здесь я использовал vs2017 и открываю окно разборки:
Вставьте описание изображения сюда
используется логика сдвига влево.

2. Беззнаковое целое положительное число сдвиг влево

int main()
{
	unsigned int i = 1;
	i = i << 3;
	return 0;
}

Вставьте описание изображения сюда
Используемая логика - сдвиг влево.

3. Сдвиг отрицательного целого числа со знаком влево

int main()
{
	int i = -1;
	i = i << 3;
	return 0;
}

Вставьте описание изображения сюда
Используемая логика - сдвиг влево.

4. Беззнаковое целое отрицательное число сдвиг влево

int main()
{
	unsigned int i = -1;
	i = i << 3;
	return 0;
}

Вставьте описание изображения сюда
Используемая логика - сдвиг влево.
Вывод : независимо от того, есть ли тип знака, положительное или отрицательное число, при смещении влево это логический сдвиг влево.

5. Сдвиг целого положительного числа со знаком вправо

int main()
{
	int i = 1;
	i = i >> 3;
	return 0;
}

Вставьте описание изображения сюда
Используемая арифметика сдвинута вправо.

6. Беззнаковое целое положительное число сдвиг вправо

int main()
{
	unsigned int i = 1;
	i = i >> 3;
	return 0;
}

Вставьте описание изображения сюда
Используется логический сдвиг вправо.
7. Знаковое целое отрицательное число сдвиг вправо

int main()
{
	int i = -1;
	i = i >> 3;
	return 0;

Вставьте описание изображения сюда
Используется арифметический сдвиг вправо.
8. Беззнаковое целое отрицательное число сдвиг вправо

int main()
{
	unsigned int i = -1;
	i = i >> 3;
	return 0;
}

Вставьте описание изображения сюда
Используется логический сдвиг вправо.
Вывод: для чисел со знаком используйте арифметический сдвиг вправо при сдвиге вправо; для чисел без знака используйте логический сдвиг вправо при сдвиге вправо.
 
 
 
 
 
Примечание: в этом случае, хотя это число без знака, старший бит равен 1. Это считается числом со знаком. Используется арифметический сдвиг вправо, но при сдвиге вправо к левому добавляется 0.

int main()
{
	unsigned char i = 128;
	i = i >> 3;
	printf("%d", i);
	return 0;
}

Вставьте описание изображения сюда
Результат:
Вставьте описание изображения сюда
почему левый сдвиг логичен только влево?
Ответ: Сначала разберитесь, как разные числа хранятся в двоичном формате.
Положительные числа хранятся в примитивном двоичном формате. Например, 3, хранятся в компьютере как: 0x3;
отрицательные числа хранятся в компьютере в соответствии с их дополнением, например -3, хранятся в компьютере как 0xfffffffb.

Компьютер хранит отрицательные числа в виде дополнений. Когда беззнаковое число сдвигается влево, его знаковый бит не изменяется, поэтому используется логический сдвиг влево; но при сдвиге вправо беззнаковое число, сдвигающееся вправо, не изменяет свой знаковый бит, а знаковое число должно обеспечивать его Знаковый бит остается неизменным, поэтому можно использовать только арифметический сдвиг вправо.

Сдвиг влево эквивалентен умножению на 2. Сдвиг
вправо эквивалентен делению на 2. Сдвиг
влево не меняет его значения.
Сдвиг лучше, чем умножение и деление . Он быстрее.

3. Битовый оператор

& // Поразрядное И
| // Поразрядное ИЛИ
^ // Поразрядное ИЛИ.
Их операнды должны быть целыми числами.

Более интересный вопрос: найти количество хранимых в памяти двоичных единиц в целом числе?

  1. Общая идея состоит в том, чтобы сделать цикл, сдвинуть номер один за другим вправо и считать с помощью 1 &. Внутренний метод нужно зациклить 32 раза и его можно оптимизировать. Сначала прикрепите этот код:
int main()
{
	int num = 99;
	int i = 0;
	int count = 0;//计数
	for (; i < 32; i++)
	{
		if (1 == ((num >> i) & 1))
		{
			count++;
		}
	}
	printf("%d\n", count);
	return 0;
}

2. Еще более сложная идея: сделать петлю, приравняв число к &числу минус 1.
Первый раз: Счет +1
Вставьте описание изображения сюда
Второй раз: Счет +1
Вставьте описание изображения сюда
Третий раз: Счет +1
Вставьте описание изображения сюда
Четвертый раз: Счет +1
Вставьте описание изображения сюда

Наконец, число 0, цикл останавливается, и число имеет 4 единицы в двоичном формате.
Прикрепленный код:

int main()
{
	int num = 99;
	int count = 0;//计数
	while (num)
	{
		count++;
		num = num & (num - 1);
	}
	printf("%d\n", count);
	return 0;
}

Результат:
Вставьте описание изображения сюда

4. Оператор присваивания

  1. Составной оператор

+ =
- =
* =
/ =
% =
>> =
<< =
& =
| =
^ =

Примечание: все такие четные уравнения просматриваются справа налево.
Такие как:

int main()
{
	int a = 3;
	int i = 10;
	int j = 20;
	a *= i + j;
	printf("%d\n", a);
	return 0;
}

Результат вывода: 90.
 

5. Унарный оператор

!                                逻辑反操作
-                            
+
&                                 取地址
sizeof                            操作数的类型长度(字节为单位)(不是函数)
~                                 按二进制位进行取反
- -
++
 *
(类型)                           强制类型转换

Примечание. Любую переменную можно понять с трех точек зрения: пространство переменных, содержимое переменной и адрес переменной .
Изменилось то, как мы рассматриваем данные.
Тип определяет: значение переменной, способ ее просмотра и объем свободного места .

6. Операторы отношения

>
>=
<=
!=
==

7. Логические операторы

&&
||

 
 
 
Примечание:
логическое И: если первая половина не установлена, то запускать последнюю не нужно.
Логическое ИЛИ: если первый тайм установлен, последний запускать не нужно.

 
 
 
8. Знак условной операции

Также называется тернарным оператором

Например a>b?a:b, это означает, что если a больше, чем b, выведите a, иначе выведите b.
 
 
 
9. Выражение-запятая.

В выражении с запятой содержимое запятой выполняется последовательно, и последнее выражение используется как значение выражения.

int main()
{
	int b = (1, 2, 3, 4, 5, 6, 7, 8);
	printf("%d\n", b);
	return 0;
}

Окончательный результат: b = 8.

 
 
 
 
 
 
Приоритет и ассоциативность операторов.
Обычно не пишите особо сложных выражений.

Неявное преобразование типа:

Зачем нам целочисленное продвижение?
Поскольку вычисление выражения выполняется в соответствующем арифметическом устройстве ЦП, длина байта операнда целочисленного арифметического блока ЦП (ALU) обычно равна длине байта int, а также длине регистра общего назначения ЦП.

Как осуществить целочисленное продвижение?
Целостное продвижение продвигается в соответствии со знаковым битом типа данных переменной.

① Если это беззнаковое число: целочисленное продвижение напрямую дополняет все нули.
②Если это число со знаком: дополните знаковый бит (см. Тип переменной).
Число имеет тип char, и при работе с операторами происходит интегральное продвижение.

рекомендация

отblog.csdn.net/w903414/article/details/106174493