ПрограммированиеEmbedded разработчик, Backend разработчик

Поясните устройство и работу оператора условия (тернарного оператора) в C. Какие подводные камни связанны с преобразованием типов и побочными эффектами, встречающиеся при его использовании?

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

Ответ

Тернарный условный оператор (?:) позволяет вычислять и возвращать одно из двух выражений в зависимости от условия:

result = cond ? expr_true : expr_false;
  • Тип результата определяется по общим правилам "usual arithmetic conversions" (если оба выражения — числа) либо по типу большего операнда (для указателей и структур).
  • Оба выражения должны быть совместимы по типу. Если они разного типа, компилятор попытается привести их к общему типу, иногда с неожиданными результатами или потерей данных.
  • Тернарный оператор допускает присутствие побочных эффектов в обоих выражениях, но вычисляется только то из них, которое требуется по условию.
  • При сложных выражениях возникают проблемы двусмысленности или непредсказуемого поведения (особенно при вложенности или использовании с макросами и функциями).

Пример

int a = 10, b = 0; int max = (a > b) ? a : b; // max = 10

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

"Если два выражения тернарного оператора возвращают объекты разного типа (например, указатель и ноль), каков будет тип результата?"

Многие утверждают, что компилятор всегда "угадывает" тип результата. На самом деле, если одно из выражений — указатель, а другое — 0 или NULL, то результат будет иметь тип указателя. Если же разница более сложная — например, возвращается указатель разного типа или тип int и тип enum — возможна неочевидная потеря информации, а иногда компилятор выдаёт ошибку.

struct node *p = NULL; void *v = cond ? p : NULL; // ok void *z = cond ? p : 0; // ok int i = cond ? 0 : "abc"; // ошибка: несовместимые типы

Примеры реальных ошибок из-за незнания тонкостей темы


История

В крупном проекте с кросс-компиляцией используемый выражение cond ? ptr : 0 возвращало указатель при одном компиляторе и int при другом (где 0 интерпретировался строго как int). Система падала при попытке использовать результат как указатель.


История

В финансовом пакете, где функции возврата использовали тернарный оператор с "голыми" литералами (cond ? 0.0 : 1), тип результата по неосторожности стал double, хотя ожидался int, из-за чего возникли ошибки сравнения и печати.


История

В одной из библиотек производился вызов с выражением побочного эффекта: flag ? inc(x) : dec(x). При рефакторинге скрытая ошибка: оба выражения вызывали функцию (со своими side-effects), хотя ожидалось, что выполнится только одно. Путаница с вложенными макросами привела к двойному изменению значения, что обнаружили только в процессе детального тестирования.