ПрограммированиеC разработчик, системный программист

Объясните, как работает оператор запятая (comma operator) в языке C. Когда его имеет смысл использовать, с какими ошибками часто сталкиваются разработчики при его применении и какие есть неожиданные эффекты?

Проходите собеседования с ИИ помощником Hintsage

Ответ

Оператор запятая , в языке C объединяет два (или более) выражения, вычисляя их по очереди слева направо и возвращая значение последнего выражения:

int x = (f(), g()); // вызываются f() и g(), x == результат g()

Наиболее часто применяется в циклах for:

for(int i = 0, j = 10; i < j; ++i, --j) { ... }

Когда полезно:

  • Когда нужно коротко записать несколько выражений, особенно в инициализации или инкрементации.

Подводные камни:

  • Приоритет запятой очень низкий, это важно в сложных выражениях;
  • Использование вне контекста (например, в return или присваивании) может привести к неожиданным результатам;
  • Использование без скобок может спутать программиста и компилятор.

Вопрос с подвохом

Вопрос: Что напечатает следующий код?

int a = 1; int b = (a = 2, a + 3); printf("%d ", b);

Ответ: Выведет "5". Сначала присваивается a=2, после чего выражение a+3 вычисляет 2+3=5, и именно это значение будет присвоено переменной b.


Примеры реальных ошибок


История

В проекте на C разработчик использовал оператор запятая без скобок в функции возврата:

return x++, y++;

Ожидалось, что функция вернёт y+1, но фактически return воспринимает только результат выражения справа, а слева x++ вычисляется отдельно. Это сбило код пользователя, так как результат return оказался не тем, что задумывалось.


История

В одном цикле for программист случайно поставил запятую вместо точки с запятой:

for(i=0, i<10, i++) ...

Программа скомпилировалась, но цикл выполнился всего один раз, так как выражение i<10, i++ в условиях for всегда возвращает значение последнего выражения (i++), а не условие продолжения цикла.


История

При написании макросов один из разработчиков определил:

#define DO(x, y) x, y int v = DO(f(), g());

Ожидал, что обе функции вызовутся, но забыл поставить скобки. В итоге вызвалось только f(); значение g() не присвоено v. Правильное использование: #define DO(x, y) ((x), (y)).