Страница будет перезагружена, если вы не хотите использовать javascript
Да, реализация C (не просто компилятор - включены стандартная библиотека, поддержка ссылок и поддержки времени выполнения и / или все, что используется для реализации C), которая производит такое же наблюдаемое поведение 1 sup>, как и соответствующая абстрактная машина. Все остальные требования и параграфы в стандарте не являются просто дополнительными. Они определяют поведение абстрактной машины, поэтому они способствуют описанию того, каким должно быть наблюдаемое поведение.
Да, программа, которая не имеет наблюдаемого поведения, может быть оптимизирована для программы, которая просто возвращает. Обратите внимание, что стандарт на самом деле не включает статус выхода в наблюдаемом поведении , поэтому xor eax, eax
технически не требуется. Тем не менее, это скорее всего случайный дефект в стандарте, а не намерение.
1 sup> Поведение программ - не единственное, что требуется в спецификации. Реализации также должны документировать, например, различное поведение, определяемое реализацией. Таким образом, эта гипотетическая реализация C, которая ведет себя идентично некоторой абстрактной машине, также должна включать в себя необходимую документацию.
Пример:
int main() {
int a;
for (int i=INT_MAX; i>=0; i--) {
a = i;
}
printf("%d\n", a);
return 0;
}
Единственное наблюдаемое поведение это то, что он будет печатать 0
за один раз. Таким образом, компилятор может оптимизировать цикл таким образом:
int main() {
printf("%d\n", 0);
return 0;
}
Что это означает по сути, что вы не можете использовать пустые циклы для добавления задержек, потому что они могут быть оптимизированы не производит задержки вообще.
ИМХО, самый драматичный побочный эффект, если компилятору разрешено предполагать, что в программе не может быть неопределенного поведения.
Второй пример:
int main() {
struct {
int a[16];
int b[16];
} s;
for (i=0; i<16; i++) {
s.a[i] = i;
s.b[i] = 2 * i;
}
for (i=0; i<32; i++) {
printf(" %d", s.a[i]); // UB array access past upper bound
}
printf("\n");
return 0;
}
Наивный компилятор должен отображать все числа от 0 до 31, потому что мы знаем, что массивы s.a
и s.b
должны быть смежными, а арифметика указателей должна давать &(s.b[0]) == &(s.a[16])
, Но оптимизирующий компилятор может заметить, что значения s.b
никогда не используются в наблюдаемом поведении, если не задействован ни один UB, и он свободен для оптимизации доступа к массиву s.b
и даже для оптимизации члена b
. Здесь следует ожидать аварийных или случайных значений ... Хуже того, действительно умный компилятор может заметить, что в цикле печати есть прошлые связанные обращения. С этого момента поведение программы не определено, и компилятор может, например, остановить цикл после печати 16-го значения. Нет ошибок, но напечатано только 16 значений ...