Арифметические и логические операторы

Урок систематизирует операторы C: арифметику, сравнение, логику и инкремент, а также объясняет коварные места — целочисленное деление, остаток и приоритет операций.
В C деление двух целых даёт целое: 7 / 2 равно 3, а не 3.5. Дробная часть отбрасывается. Это не баг, а следствие типизации, и источник бесчисленных ошибок у новичков.

Арифметические операторы привычны: + - * / и оператор остатка %. Но в C тип операндов решает всё. Если оба операнда целые, то и результат деления будет целым с отброшенной дробной частью:

int a = 7, b = 2;
printf("%d\n", a / b);      // 3, а не 3.5
printf("%d\n", a % b);      // 1 — остаток от деления
printf("%f\n", 7.0 / 2);    // 3.5 — хотя бы один операнд дробный

Оператор остатка % отвечает на вопрос «что останется после деления нацело». 7 % 2 равно 1. Он невероятно полезен: проверка чётности (n % 2 == 0), зацикливание индекса, разбиение числа на цифры.

Операторы сравнения (== != < > <= >=) возвращают целое: 1 если истина, 0 если ложь. В C нет отдельного типа «логическое значение» в классическом смысле — любое ненулевое число считается истиной, ноль — ложью. Логические операторы: && (И), || (ИЛИ), ! (НЕ).

Как работает под капотом

Два важнейших механизма: приоритет операторов и сокращённое вычисление логики. Приоритет определяет порядок: * и / выполняются раньше + и -, как в математике. А логические && и || вычисляются «лениво» — слева направо, с ранним выходом.

Выражение:  2 + 3 * 4

Шаг 1:  3 * 4  =  12      (умножение раньше сложения)
Шаг 2:  2 + 12 =  14

Логика && (короткое замыкание):
   (x != 0) && (10 / x > 1)
        |              |
   если ЛОЖЬ ----------+--> правая часть НЕ вычисляется
   (защита от деления на ноль!)

Сокращённое вычисление — не просто оптимизация, а инструмент защиты. В выражении x != 0 && 10 / x > 1, если x равен нулю, левая часть ложна, и правая (с делением на ноль) вообще не выполнится.

Частые ошибки

  • Целочисленное деление там, где ждали дробь. double avg = sum / count; при целых sum и count сначала делит нацело, теряя дробь, и только потом сохраняет в double.
  • Путаница = и ==. if (x = 0) присваивает ноль и всегда ложно; if (x == 0) сравнивает.
  • Расчёт на приоритет наугад. a & b == c работает не так, как кажется: == приоритетнее побитового &.
  • Остаток от отрицательных чисел. Знак результата % в C совпадает со знаком делимого, что может удивить.

Best practices

  • Ставьте скобки, даже когда приоритет «и так понятен». Скобки бесплатны, а читаемость и защита от ошибок — нет.
  • Чтобы получить дробное деление целых, приводите тип явно: (double)sum / count.
  • Используйте сокращённое вычисление осознанно: ставьте проверку-защиту слева от &&.

Целочисленное деление и остаток одинаково работают в Python (оператор // и %), поэтому логику легко проверить там же.

# Разбиваем число на цифры через % и // — как в C через % и /
n = 1234
digits = []
while n > 0:
    digits.append(n % 10)   # последняя цифра
    n = n // 10             # отбрасываем её (в C это n / 10 для int)
print("Цифры справа налево:", digits)
print("Сумма цифр:", sum(digits))

Та же логика на Python ▶ — связка «остаток + деление нацело» одинакова в обоих языках. В C для целых n / 10 ведёт себя как // в Python.

Побитовые операторы

Помимо логических, в C есть побитовые операторы, работающие с отдельными битами числа: & (И), | (ИЛИ), ^ (исключающее ИЛИ), ~ (инверсия) и сдвиги <<, >>. Их легко спутать с логическими && и ||, но смысл другой: 5 & 3 сравнивает биты и даёт 1, а 5 && 3 — это логическое И, дающее 1 как «истина И истина». Побитовые операции широко применяют там, где важна каждая копейка памяти или скорость: флаги-битмаски (несколько булевых настроек в одном числе), работа с регистрами устройств, быстрое умножение и деление на степени двойки через сдвиги. Это одна из причин, по которой C незаменим в системном и встраиваемом программировании.

Итоги

Операторы C чувствительны к типам: деление двух целых отбрасывает дробь, а оператор % даёт остаток. Сравнения и логика возвращают 0 или 1, причём && и || вычисляются с коротким замыканием, что можно использовать для защиты. Главные ловушки — целочисленное деление, путаница = и == и приоритет операций; лечатся явным приведением типа и скобками.

Проверьте себя
1. Чему равно 7 / 2 в C, если оба числа типа int?
A3.5
B3
C4
D1
2. Как короткое замыкание оператора && защищает выражение x != 0 && 10 / x > 1?
AОно ускоряет деление
BЕсли x равен 0, левая часть ложна и правая (с делением на ноль) не вычисляется
CОно округляет результат деления
DОно меняет порядок операндов