Разница в поведении (GCC и Visual С++)
Рассмотрим следующий код.
#include <stdio.h>
#include <vector>
#include <iostream>
struct XYZ { int X,Y,Z; };
std::vector<XYZ> A;
int rec(int idx)
{
int i = A.size();
A.push_back(XYZ());
if (idx >= 5)
return i;
A[i].X = rec(idx+1);
return i;
}
int main(){
A.clear();
rec(0);
puts("FINISH!");
}
Я не мог понять, почему код дает ошибку сегментации в Linux (IDE используется: Code::Blocks), тогда как в Windows (Используется IDE: Visual С++), это не так.
Когда я использовал Valgrind, чтобы проверить, что на самом деле проблема, я получил этот результат.
Я получил Invalid write of size 4
в четырех разных местах. Тогда почему не произошел сбой кода при использовании Visual С++?
Я что-то пропустил?
Ответы
Ответ 1
Рекурсивный вызов rec()
может изменить вектор, когда вы присваиваете ему значение.
Что произойдет, если вы замените
A[i].X = rec(idx+1);
с
int tmp = rec(idx+1);
A[i].X = tmp;
?
Кроме того, просто для обобщения полезных комментариев: порядок оценки операнда операции =
не указан, и поскольку вектор не был предварительно выделен, при рекурсивном вызове rec()
может произойти несколько изменений размера, что делает недействительным любой итератор к значениям в векторе.
Ответ 2
Я получаю ошибку * для объекта 0x300180: некорректная контрольная сумма для освобожденного объекта - объект, вероятно, был изменен после освобождения. * ", когда я запускаю этот код.
Как я помню, A[i].X = rec(idx+1)
имеет три точки последовательности. Когда оператор [] вызывается на A, когда rec вызывается и в конце. Но порядок первых двух не определен. Поэтому, если g++ сначала вычисляет A[i]
, а затем вызывает rec(idx+1)
, тогда, когда rec
возвращает ссылку, возвращаемую A[i]
, может быть недействительной путем перераспределения векторной внутренней памяти. В разделе VС++ это может быть сначала оценено rec(idx+1)
, поэтому все вызовы push_back
выполняются спереди, что означает, что вызовы A[i]
относятся к правильному блоку памяти. В качестве альтернативы, это может сделать то же самое, и вы просто не сталкиваетесь с проблемой segfault... что одна из проблем поведения undefined.
Изменение std::vector<XYZ> A;
до std::vector<XYZ> A(10);
зарезервирует достаточно места для 10 элементов. Это предотвратит вашу конкретную реализацию rec
от необходимости перераспределения и исправляет ошибку на моем конце.
Ответ 3
Вы используете int i = A.size()
И затем вы индексируете свою структуру как массив, но используя значение размера. Вам нужно уменьшить его на 1, например. A[i-1].X = rec(idx+1);
А моя ошибка - я не учитывал вектор push_back.