Подтвердить что ты не робот

Ошибка компиляции С++?

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

#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++ С++ 14 с опцией -O2.

4b9b3361

Ответ 1

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

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

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

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

jmp .L6

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

Хотя это агрессивная оптимизация, важно отметить, что, как утверждает стандарт С++, поведение undefined:

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

Что по существу означает, что все возможно, и оно отмечает (внимание мое):

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

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

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

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

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

Ответ 2

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

Ответ 3

У вас есть следующее:

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:

Чтобы прояснить, что происходит, Lets Break Down The Iteration Of Your For Loop:

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

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

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

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

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

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

Ответ 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];
}