Як пріоритет операторів C ламає ваш код (і як це виправити)

1

Ви пишете те, що маєте на увазі. Компілятор читає, що хоче.

Якщо ви коли-небудь дивилися на рядок коду C, будучи впевненим, що вона робить одне, а потім спостерігали, як вона робить зовсім інше, ви зустрілися з пріоритетом операторів. Це не є помилкою. Це особливість мови, яка кусає навіть досвідчених розробників.

Візьмемо цю просту арифметику:

x=5+3*6;

У школі математики кажуть: спочатку додавання. У C кажуть: ні. Множення та розподіл знаходяться на вищому рівні, ніж додавання та віднімання. Компілятор спочатку множить 3 на 6. Потім додає 5. X отримує 23. Не 48. Ви щойно втратили бали на тесті. Або що гірше на продакшен-розгортанні.

Помилка поглиблюється, коли ви змішуєте оператори із синтаксисом.

Подивіться на це оголошення:

char *a[10];

Чи є вказівником на масив з 10 символів? Чи це масив із 10 покажчиків на символи?

Не знаючи конкретних правил пріоритету, ви гадаєте. У C квадратні дужки [] пов’язують суворіше, ніж покажчики *. Таким чином, це масив із 10 покажчиків на символи.

Це має значення, тому що C не розуміється як англійська. Він розуміється як жорстка ієрархія.

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

Розглянемо арифметику покажчиків, змішану із доступом до структур.

*p.i = 10;

Це не працює так, як очікується. Оператор точка . має вищий пріоритет, ніж оператор розіменування *. Компілятор намагається отримати доступ до члена i об’єкта, на який вказує p, до розіменування p. Але p – це покажчик, а не структура. Компілятор спотикається. Або, що гірше, він інтерпретує пам’ять таким чином, що призводить до пошкодження даних.

Вам потрібно наполягти на своєму.

(*p).i = 10;

Дужки перемагають. Завжди.

Ієрархія операторів

Керніган та Рітчі, архітектори C, виклали пріоритет у своєму класичному тексті. У таблиці нижче показано порядок. Верхній рядок – найвищий пріоритет. Нижня – найменший.

  1. Купки () та Індексація []
  2. Зліва направо.
  3. Унарні оператори ++, --, +, -, !, ~, (type), * (розіменування), & (адреса), sizeof
  4. Праворуч наліво.
  5. Мультиплікативні *, /, %
  6. Зліва направо.
  7. Аддитивні +, -
  8. Зліва направо.
  9. Зсув <<, >>
  10. Зліва направо.
  11. Відносини <, <=, >, >=
  12. Зліва направо.
  13. Рівність ==, !=
  14. Зліва направо.
  15. Побітове І &
  16. Зліва направо.
  17. Побітове що виключає АБО ^