programowanieProgramista C

Opisz różnice między operacjami inkrementacji (i++) i (++i) w języku C. Jaka jest ich semantyka, kiedy stosować każdą z nich i jakie mogą być ich niebezpieczeństwa w złożonych wyrażeniach?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

W języku C operacje inkrementacji postfiksowej (i++) i prefiksowej (++i) zwiększają wartość zmiennej o 1, ale zwracana wartość różni się:

  • i++ (postfiksowa): najpierw zwraca bieżącą wartość, a potem ją zwiększa.
  • ++i (prefiksowa): najpierw zwiększa wartość, a potem zwraca nową wartość.

Przykład:

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

W prostych wyrażeniach różnica nie ma znaczenia, ale w złożonych (na przykład z wieloma inkrementacjami naraz) może prowadzić do niezdefiniowanego zachowania.

Pytanie z pułapką

Jaką wartość będzie miała zmienna a po wykonaniu wyrażenia int a = i++ + ++i;, jeśli i = 1?

Odpowiedź:

Obliczenia zależą od kolejności wykonywania operandów, której standard nie gwarantuje, a także prowadzą do niezdefiniowanego zachowania, ponieważ zmienna i jest modyfikowana więcej niż raz między kolejnymi użyciami wartości. Nie należy tak pisać!

Przykład takiego kodu:

int i = 1; int a = i++ + ++i; // niezdefiniowane zachowanie! Nie stosuj tego!

Przykłady rzeczywistych błędów z powodu braku wiedzy na ten temat


Historia

W dużym projekcie obliczenie indeksu w tablicy było pisane jako arr[i++] = getValue(++i); — programista chciał zachować starą wartość, jednocześnie uzyskując nową. W zależności od kompilatora zachowanie różniło się: czasami jedna wartość nadpisywała drugą, czasami program się zawieszał. Przyczyna — niedopuszczalne wielokrotne zmiany i w jednym wyrażeniu.


Historia

W projekcie embedded wartość licznika była zwiększana jako część złożonego wyrażenia: if (buffer[i++] == TERMINATOR && ++i < SIZE) ... — na „sprzęcie” czasami uzyskiwano nieprawidłowy indeks z powodu różnego porządku obliczeń, co prowadziło do odczytu niezainicjowanych danych.


Historia

Przy przenoszeniu kodu na inny kompilator różnica w implementacji kolejności obliczeń operatorów doprowadziła do tego, że pętla typu while (arr[i++] && i < MAX && arr[++i]) zaczęła zachowywać się nieprzewidywalnie. Błąd został znaleziony dopiero w wyniku fazy testowania na urządzeniu klienta.