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

Разница в поведении (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 С++?

Я что-то пропустил?

4b9b3361

Ответ 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.