Здравствуйте. Сегодня я пробовал сдавать задачи без своей visual studio 2012, используя ideone.com. У меня не заработало Binary Search Tree, написанное на векторе. Точнее, оно выдавало неправильные ответы. После некоторого упрощения, тот же код стал давать ошибку исполнения.
После нескольких тщетных попыток понять в чем дело я скопировал код в студию, поставил несколько точек останова, начал отладку. Все верно. Убрал точки, запустил — а оно работает.
Поменял конфигурацию Debug на Release, запустил — ошибка исполнения.
Попробовал скомпилировать на MinGW 4.8.1 (tdm-2) — ошибка исполнения, независимо от наличия флага -O.
Cделал пример как можно меньше, но даже после этого результат прежний — signal 11 (SIGSEGV), обращение к не принадлежащей области памяти.
#include <vector>
namespace Ex {
std::vector<int> next; // next[i] = index of next element. -1 if it's not exist
int rec(int ind = 0) {
if( ind == -1 ) {
next.push_back(-1);
return next.size() - 1;
}
next[ind] = rec(next[ind]);
return ind;
}
}
int main() {
Ex::next.push_back(-1);
for(int i = 0; i < 20; i++)
Ex::rec();
// expected Ex::next states
// -1
// 1 -1
// 1 2 -1
// 1 2 3 -1
// ...
// 1 2 3 ... 20 -1
return 0;
}
Я уже понял, что на ideone мне лучше не писать, но все-таки, может кто-то объяснит в чем дело, или все же на этот раз виновен компилятор?) Самому не очень верится, т.к. впервые вижу чтобы не работало на обоих.
Проблема в строке:
Чтобы проблемы не было, код можно написать так:
Как по мне, это баг компилятора. Однако есть тень сомнения, а нету ли здесь случайно "неоднократного изменения переменной в пределах одной точки следования", что вызывает UB? (Это может быть связано с тем, что адрес переменной next[ind] может измениться после вызова функции и реалокации содержимого вектора). А может быть и в рекурсии дело...
По стандарту всё ок, компилятор вправе вычислить сначала ссылку next[ind], а затем вычислить rec(...) rec может сделать push_back который может перевыделить память и тем самым ссылка становится невалидной. Сам наступал на эти грабли и где-то здесь уже писал.
Тут помогает вывести месь массив на экран после каждой итерации цикла. Тогда видно, что он заполняется как-то не так как ожидается.
И как уже написал MrDindows дело в том что, адрес куда записывать next[ind] был один а после выполнения rec(...) стал другим, так как там вектор увеличил размер и перевыделил память, и соответственно адреса элементов поменялись.
Полагаю, что это ожидаемый результат, так как приоритет [] выше чем у оператора = http://ru.cppreference.com/w/cpp/language/operator_precedence
Получается сначала вычислется ссылка next[ind], а потом то что справва от =. Ну и на какой-то итерации это рантаймит :)
Утром в голову пришел гораздо менее синтетический пример — DSU с авторасширением.
Теперь у меня стабильный RE на GNU C++ / MINGW и такой же стабильный AC на msvc2010/2012.
В прошлой правке код, тест и скрины вывода.
Меня интересует, верно ли утверждение — "присвоение a[i] = f(), где "f" вызывает реаллокацию "a" дает Undefined behavior (ok/RE) на msvc, тогда как на g++ это RE". По-моему это так, потому что DSU на вижаке правильно работает, а в примере из поста фейлит, как и g++.
Да.
Спасибо, понял свой затуп.
Самое комичное, что у меня в данный момент два вроде как разных по содержанию поста в блоге (с одинаковым названием и разницей в два года), и в обоих мне подсказали одно и то же UB. Похоже, опыт в этих делах плохо помогает, если специально не писать каких-то экстремальных кодов (MrDindows тоже на компилятор подумал). Надо специально изучать примеры красиво выглядящего UB разных видов, чтобы не допускать.
Typical C++
<holywar>
You mean — "Mysterious high-level programming language which prevent you from losing the understanding of actual operations" ? :)</holywar>
I'm on a quest to insert "Typical %something%" comments in every place that seems fit.
Typical beatoriche