Компиляции C++ ошибка?


у меня есть следующий код:

#include <iostream>
#include <complex>
using namespace std;

int main() {
    complex<int> delta;
    complex<int> mc[4] = {0};

    for(int di = 0; di < 4; di++, delta = mc[di]) {
        cout << di << endl;
    }

    return 0;
}

Я ожидаю, что он выведет "0, 1, 2, 3" и остановится, но он выводит бесконечную серию "0, 1, 2, 3, 4, 5, ....."

похоже на сравнение di<4 не работает и всегда возвращает true.

Если я просто закомментируйте ,delta=mc[di], Я получаю "0, 1, 2, 3", как обычно. В чем проблема с невинным заданием?

Я использую Ideone.com g++ C++14 с опцией-O2.

4 71

4 ответа:

это связано с неопределенным поведением, вы получаете доступ к массиву mc вне пределов на последней итерации цикла. Некоторые компиляторы могут выполнять агрессивную оптимизацию цикла вокруг предположений об отсутствии неопределенного поведения. Логика была бы аналогична следующей:

  • ссылке mc out of bounds-это неопределенное поведение
  • предположим, что нет неопределенного поведения
  • di < 4 всегда верно, так как в противном случае mc[di] будет вызвать неопределенное поведение

GCC с оптимизацией включен и с помощью -fno-aggressive-loop-optimizations флаг приводит к исчезновению поведения бесконечного цикла (посмотреть его в прямом эфире). В то время как живой пример с оптимизацией, но без-fno-aggressive-loop-optimizations демонстрирует поведение бесконечного цикла, которое вы наблюдаете.

A godbolt живой пример кода показывает di < 4 проверка удаляется и заменяется на и безусловный jmp:

jmp .L6

это почти идентично случаю, описанному в предварительно 4.8 ССЗ ломает сломанный спецификаций контрольных показателей 2006. Комментарии к этой статье превосходны и хорошо стоит прочитать. Он отмечает, что clang поймал дело в статье с помощью -fsanitize=undefined который я не могу воспроизвести для этого случая, но gcc с помощью -fsanitize=undefined у (посмотреть его в прямом эфире). Вероятно, самая печально известная ошибка вокруг оптимизатора, делающего вывод вокруг неопределенное поведение-это удаление нулевого указателя ядра Linux.

хотя это агрессивная оптимизация, важно отметить, что, как стандарт C++ говорит неопределенное поведение:

поведение, к которому настоящий международный стандарт не предъявляет никаких требований

что по существу означает, что все возможно, и он отмечает (выделено мной):

[...]Допустимый неопределенное поведение колеблется от игнорирования ситуации полностью с непредсказуемым результатом, чтобы вести себя во время трансляции или выполнение программы в документированном виде характерно для среды (с выдачей или без выдачи диагностическое сообщение), на завершение перевода или исполнения (с выдачей диагностического сообщения).[...]

чтобы получить предупреждение от gcc нам нужно переместить cout вне цикла, а затем мы см. следующее предупреждение (посмотреть его в прямом эфире):

warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
     for(di=0; di<4;di++,delta=mc[di]){ }
     ^

что, вероятно, было бы достаточно, чтобы предоставить ОП достаточно информации, чтобы выяснить, что происходит. Подобная несогласованность типична для тех типов поведения, которые мы можем наблюдать при неопределенном поведении. Чтобы лучше понять, почему такой waring может быть непоследовательным перед лицом неопределенного поведения почему вы не можете предупредить при оптимизации на основе undefined поведение? хорошо читать.

Примечание -fno-aggressive-loop-optimizations документирована в примечания к выпуску gcc 4.8.

Так как вы увеличиваете di прежде чем использовать его для индекса mc, в четвертый раз через цикл вы будете ссылаться на mc[4], который находится за концом вашего массива, что, в свою очередь, может привести к неприятному поведению.

это потому, что di++ выполняется на последнем запуске цикла.

например:

int di = 0;
for(; di < 4; di++);
// after the loop di == 4
// (inside the loop we see 0,1,2,3)
// (inside the for statement, after di++, we see 1,2,3,4)

вы обращаетесь к mc [], когда di == 4, поэтому это проблема вне границ, потенциально разрушающая часть стека и разрушающая переменную di.

решение:

for(int di = 0; di < 4; di++) {
    cout << di << endl;
    delta = mc[di];
}

у вас есть это:

for(int di=0; di<4; di++, delta=mc[di]) {
  cout<<di<<endl;
}

попробуйте это вместо этого:

for(int di=0; di<4; delta=mc[di++]) {
   cout<<di<<endl;
}

EDIT:

чтобы уточнить, что происходит, давайте разбить итерацию вашего цикла For:

1-я итерация: первоначально di устанавливается в 0. Проверка сравнения: di меньше 4? Да ладно продолжайте. Приращение di на 1. Теперь di = 1. Возьмите" N-й " элемент mc[] и установите его в delta. На этот раз мы хватаем 2-й элемент, так как это индексированное значение равно 1, а не 0. Наконец-то выполнять кодовый блок / s внутри цикла for.

2-я итерация: теперь di имеет значение 1. Проверка сравнения: di меньше 4? Да и продолжайте. Приращение di на 1. Теперь di = 2. Возьмите" N-й " элемент mc[] и установите его в delta. На этот раз мы хватаем 3-й элемент, так как это индексированное значение равно 2. Наконец, выполните блок кода / s внутри цикла for.

3-я итерация: теперь di имеет значение 2. Проверка сравнения: di меньше 4? Да и продолжайте. Приращение di на 1. Теперь di = 3. Возьмите "nth" элемент mc[] и установить его в delta. На этот раз мы хватаем 4-й элемент, так как это индексированное значение равно 3. Наконец, выполните блок кода / s внутри цикла for.

4-я итерация: теперь di имеет значение 3. Проверка сравнения: di меньше 4? Да и продолжайте. Приращение di на 1. Теперь di = 4. (Вы можете видеть, куда это идет?) Возьмите" N-й " элемент mc[] и установите его в delta. На этот раз мы хватаем 5-й элемент, так как это индексированное значение равно 4. О-О, у нас проблема.; наш размер массива составляет всего 4. У Delta теперь есть мусор, и это неопределенное поведение или коррупция. Наконец, выполните блок кода / s внутри цикла for, используя "мусорную дельту".

5-й итерации. Теперь di имеет значение 4. Проверка сравнения: di меньше 4? Нет, вырваться из петли.

повреждение путем превышения границ смежной памяти (массива).