C «наблюдаемое поведение» в контексте UB «неопределенное поведение»
(Вопрос был первоначально вызван комментариями под этим ответом наЕсть ли условия гонки в этой реализации производителя-потребителя? но здесь он задается строго с точки зрения языка Си, без какого-либо параллелизма или многопоточности.)
Рассмотрим этот минимальный код:
#define BUFSIZ 10
char buf[BUFSIZ];
void f(int *pn)
{
buf[*pn]++;
*pn = (*pn + 1) % BUFSIZ;
}
int main()
{
int n = 0;
f(&n);
return n;
}
Вопрос: будет ли C"как будто" правила позволяют компилятору переписать код следующим образом?
void f(int *pn)
{
int n = *pn;
*pn = (*pn + 1) % BUFSIZ;
buf[n]++;
}
С одной стороны, вышеизложенное не изменит наблюдаемого поведения программы в том виде, в котором оно написано.
С другой стороны,f
может быть вызван с неверным индексом, возможно из другого модуля перевода:
int g()
{
int n = -1001;
f(&n);
}
В этом последнем случае оба варианта кода будут вызывать UB при доступе к элементу массива вне границ. Тем не менее, оригинальный код оставил бы*pn
при значении, передаваемом вf
(= -1001) в то время как переписанный код вступит в UB-land только после модификации*pn
(в0
).
Будет ли такая разница считаться «наблюдаемой» или, вернемся к фактическому вопросу, есть ли в стандарте C что-либо, что конкретно разрешало бы или исключало такой тип переписывания / оптимизации кода?