Проанализируйте следующий фрагмент на C и объясните, какие операции приводят к неопределённому поведению, почему компилятор может выдать неожиданный результат и как переписать код, чтобы он был корректен по стандарту C: int i = 0
Фрагмент int i = 000; сам по себе корректен и не содержит неопределённого поведения — это обычная определённая инициализация. Часто под таким вопросом понимают выражения, где одна и та же переменная и модифицируется, и читается в одном выражении без упорядочения (sequence point / sequencing) — это и вызывает UB. Примеры плохих выражений (они неопределённы по стандарту C): - i=i+++1;i = i++ + 1;i=i+++1; - i=++i;i = ++i;i=++i; - i=i+++i++;i = i++ + i++;i=i+++i++; - a[i++]=i;a[i++] = i;a[i++]=i; Почему это UB: стандарт C запрещает модификацию объекта более одного раза или модификацию и одновременное чтение его значения в одном «неупорядоченном» фрагменте выражения. В таких выражениях порядок вычислений и применения побочных эффектов не определён (в C11/C17 говорят об unsequenced/indeterminately sequenced), поэтому поведение — неопределённое. Последствия для компилятора: при наличии UB компилятор может предположить, что такие ситуации не встречаются, и выполнить оптимизации или перестановки кода, которые приведут к неожиданному результату (удаление операций, изменение порядка, выдача «странных» значений). Нельзя полагаться на то, что результат будет «интуитивным» на конкретном компиляторе. Как переписать корректно (варианты): - Для i=i+++1;i = i++ + 1;i=i+++1; — явно сохранить старое значение: int tmp = i; i = tmp + 111; или если цель просто увеличить: i += 111; или i++; - Для i=++i;i = ++i;i=++i; — просто: ++i; (или, если нужна присвоенная величина, использовать tmp) - Для i=i+++i++;i = i++ + i++;i=i+++i++; — использовать временные: int t1 = i++; int t2 = i++; i = t1 + t2; - Для a[i++]=i;a[i++] = i;a[i++]=i; — сохранить индекс отдельно: int idx = i++; a[idx] = i; или a[i] = i; i++; Общее правило: не изменяйте и не читайте одно и то же скалярное значение в одном выражении без явного упорядочения (разнесите на несколько операторов или используйте временные переменные). Это гарантирует поведение, согласованное со стандартом C.
int i = 000;
сам по себе корректен и не содержит неопределённого поведения — это обычная определённая инициализация.
Часто под таким вопросом понимают выражения, где одна и та же переменная и модифицируется, и читается в одном выражении без упорядочения (sequence point / sequencing) — это и вызывает UB. Примеры плохих выражений (они неопределённы по стандарту C):
- i=i+++1;i = i++ + 1;i=i+++1;
- i=++i;i = ++i;i=++i;
- i=i+++i++;i = i++ + i++;i=i+++i++;
- a[i++]=i;a[i++] = i;a[i++]=i;
Почему это UB: стандарт C запрещает модификацию объекта более одного раза или модификацию и одновременное чтение его значения в одном «неупорядоченном» фрагменте выражения. В таких выражениях порядок вычислений и применения побочных эффектов не определён (в C11/C17 говорят об unsequenced/indeterminately sequenced), поэтому поведение — неопределённое.
Последствия для компилятора: при наличии UB компилятор может предположить, что такие ситуации не встречаются, и выполнить оптимизации или перестановки кода, которые приведут к неожиданному результату (удаление операций, изменение порядка, выдача «странных» значений). Нельзя полагаться на то, что результат будет «интуитивным» на конкретном компиляторе.
Как переписать корректно (варианты):
- Для i=i+++1;i = i++ + 1;i=i+++1; — явно сохранить старое значение:
int tmp = i;
i = tmp + 111;
или если цель просто увеличить:
i += 111;
или
i++;
- Для i=++i;i = ++i;i=++i; — просто:
++i;
(или, если нужна присвоенная величина, использовать tmp)
- Для i=i+++i++;i = i++ + i++;i=i+++i++; — использовать временные:
int t1 = i++;
int t2 = i++;
i = t1 + t2;
- Для a[i++]=i;a[i++] = i;a[i++]=i; — сохранить индекс отдельно:
int idx = i++;
a[idx] = i;
или
a[i] = i;
i++;
Общее правило: не изменяйте и не читайте одно и то же скалярное значение в одном выражении без явного упорядочения (разнесите на несколько операторов или используйте временные переменные). Это гарантирует поведение, согласованное со стандартом C.