Почему компилятор может оптимизировать сравнение при доступе за границы массива?

bool contains(int val) {
    int arr[] = {1, 2, 3};

    for (int i = 0; i <= 3; ++i) 
        if (arr[i] == val) 
            return true; 
            
    return false;
}

#include <iostream>

int main() {    
    std::cout << contains(2);
    std::cout << contains(4);
}

Почему с оптимизацией -O2 компилятор может превратить функцию в возврат единицы?

Понятно, что тут неопределённое поведение, но непонятно, почему оно позволяет так оптимизировать код, что результат в итоге неверный.


Ответы (1 шт):

Автор решения: dIm0n

Преобразование допустимо, потому что стандарт не определяет поведение программы в этом случае, а значит может быть всё что угодно.

По какой логике компилятор мог преобразовать вызов функции в return true;?

Потому что исполнение return false; может произойти, только если arr[3] == val стало false, но с точки зрения компилятора программист предоставляет гарантию об отсутствии неопределённого поведения, а значит доступа arr[3] не может произойти. Раз не может произойти доступа arr[3], то и arr[3] == val не может стать false. Так как все оставшиеся пути выполнения функции возвращают true, можно соптимизировать её до чистого return true;.

Кстати, gcc в этом смысле более агрессивен; clang возвращает значение, в зависимости от того, меньше ли переданный аргумент минус единица тройки, получая ожидаемый результат (сравнение). С -O1 результаты верные у обоих компиляторов.


Использовались комментарии участников @user7860670, @Chorkov, @gauss.

→ Ссылка