ПрограммированиеC разработчик

Опишите различия между операциями инкремента (i++) и (++i) в языке C. Какова их семантика, когда стоит использовать тот или иной вариант и чем они опасны в сложных выражениях?

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

Ответ

В языке C операции постфиксного (i++) и префиксного (++i) инкремента увеличивают значение переменной на 1, но возвращаемое значение отличается:

  • i++ (постфикс): сначала отдаёт текущее значение, затем увеличивает его.
  • ++i (префикс): сначала увеличивает значение, затем возвращает новое значение.

Пример:

int i = 5; int a = i++; // a == 5, i == 6 int j = 5; int b = ++j; // b == 6, j == 6

В простых выражениях разница несущественна, но в сложных (например, с несколькими инкрементами за раз) может возникнуть неопределённое поведение.

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

Какое будет значение переменной a после выполнения выражения int a = i++ + ++i;, если i = 1?

Ответ:

Вычисление зависит от порядка вычисления операндов, который стандарт не гарантирует, а также приводит к неопределённому поведению (undefined behavior), потому что переменная i модифицируется более одного раза между последовательным использованием значения. Так писать нельзя!

Пример такого кода:

int i = 1; int a = i++ + ++i; // неопределённое поведение! Не используйте так!

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


История

В крупном проекте расчет индекса в массиве писался как arr[i++] = getValue(++i); — разработчик хотел сохранить старое значение, одновременно получить новое. На разных компиляторах поведение отличалось: иногда одно значение затирало другое, иногда программа крашилась. Причина — недопустимые множественные изменения i в одном выражении.


История

В embedded-проекте значение счетчика увеличивалось как часть сложного выражения: if (buffer[i++] == TERMINATOR && ++i < SIZE) ... — на «железе» иногда получался неверный индекс из-за разного порядка вычисления, приводя к чтению неинициализированных данных.


История

При портировании кода на другой компилятор разница в реализации порядка вычисления операндов привела к тем, что цикл типа while (arr[i++] && i < MAX && arr[++i]) стал вести себя непредсказуемо. Баг нашли только по результатам фазы тестирования уже на устройстве клиента.