Можно ли поменять местами массивы структур (за линейное время)? - PullRequest
1 голос
/ 11 февраля 2012

У меня есть небольшая программа, вычисляющая силы планет друг на друге.В моей программе есть два массива структур, один из которых содержит позиции и скорости до итераций, а другой - их позиции и скорости после итераций.

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

Рассмотрим этот пример:

typedef struct { int a; } Foo;

int main()
{
   Foo bar[8], baz[8];

   Foo *temp = baz;
   baz = bar;   //ISO C++ forbids the assignment of arrays
   bar = temp;  //incompatible types in assignment of Foo* to Foo[8]
}

Это то, что я хотел бы сделать.Это, безусловно, будет быстрее, чем цикл for от 1 до N.

Ответы [ 6 ]

5 голосов
/ 11 февраля 2012

Вы должны рассмотреть возможность использования std::vector, который можно менять в постоянное время:

std::vector<Foo> bar(8), baz(8);

std::swap(bar, baz);

Или, если вы не хотите этого делать и вместо этого хотите вручную управлять своей памятью, вы можете использовать new[] чтобы получить указатель на массив в бесплатном хранилище и поменять местами указатели, когда вы хотите поменять местами массивы.

Если у вас есть массивы в стеке, единственный способ сделать это без заменыкаждый элемент будет создавать массивы в стеке и вместо использования массивов использовать указатели на массивы:

Foo bar[8], baz[8], *pbar = bar, *pbaz = baz;

// ...
// this code only using pbar and pbaz
// ...

// swap the pointers
std::swap(pbar, pbaz);

// ...
// use pbar and pbaz some more
// ...
2 голосов
/ 11 февраля 2012

Здесь вы ошиблись:

, поскольку массив является указателем

Это не так.Массивы распадаются на указатель, но это не одно и то же.

Однако вы можете легко сделать то, что вам нужно, фактически получая указатели из этих массивов.

   Foo bar[8], baz[8];

   // load bar up with valid data
   Foo *data = bar;
   Foo *garbage = baz;

   // compute next step

   std::swap(data,garbage);

Также рассмотрите возможность использования std::array вместо необработанных массивов (он обладает свойствами, более совместимыми с другими типами в C ++ и менее «специальным», чем необработанные массивы).Тогда, когда вы используете указатели на эти массивы, размер массива не будет отбрасываться, как это делается для указателей на необработанные массивы.

   std::array<Foo,8> bar, baz;

   std::array<Foo,8> *data = &bar;
   std::array<Foo,8> *garbage = &baz;

   // compute next step

   std::swap(data,garbage);

Если вам нужны массивы динамического размера, используйте std::vector, а затемВы можете просто поменять векторы напрямую, а не поменять указатели на векторы, потому что поменяются векторы, чтобы поменять местами указатели внутри векторов.

   std::vector<Foo> data, garbage;

   // compute next step

   std::swap(data,garbage);
0 голосов
/ 11 февраля 2012
   Foo bar[8], baz[8];

Тогда

   Foo* arr1 = bar, arr2 = bax;

Забудьте о baz & bar, работайте только с arr1 и arr2.

Swap:

  int* temp = arr1;
  arr1 = arr2;
  arr2 = temp;
0 голосов
/ 11 февраля 2012

Вы можете объявить свои массивы как временные, а также объявить указатели - которые вы будете использовать для остальной части функции.

Эти понтеры можно легко поменять местами.

int main()
{
   Foo temp1[8], temp2[8];
   Foo *bar = temp1, *baz = temp2;

   Foo *temp = baz;
   baz = bar;  //no problems now 
   bar = temp;  
}
0 голосов
/ 11 февраля 2012

Вы можете использовать memcpy:

int main()
{
   Foo bar[8], baz[8], temp[8];

   memcpy(temp, baz, sizeof(Foo) * 8);
   memcpy(baz, bar, sizeof(Foo) * 8);
   memcpy(bar, temp, sizeof(Foo) * 8);
}
0 голосов
/ 11 февраля 2012

Проблема в том, что вы используете выделенный стеком массив.

Когда вы говорите Foo bar[8];, вы создаете пространство для восьми Foo структур в текущем кадре стека. Поэтому причина, по которой вы не можете назначить его чему-то другому, заключается в том, что ему придется каким-то образом выйти за пределы кадра, избегая управления памятью и определяя область его действия.

Требуется динамически размещаемый массив - Foo* bar = new Foo[8];. Когда вы закончите с этим, delete[] bar;. Разница здесь в том, что теперь указатель находится в стеке, но фактическое содержимое находится в куче - так что вы можете изменить расположение ссылок на указатель, не беспокоясь о перемещении самого указателя.

Другой вариант - использовать класс с конструктором копирования и operator=, например std::vector, std::list или std::deque.

Кстати, процедура копирования содержимого массива - линейное время. Назначение указателя является постоянным временем (O(1)), потому что не имеет значения, насколько велик ваш массив - это всегда одна операция для перемещения указателя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...