【问题标题】:Is it possible to swap arrays of structs (in linear time)?是否可以交换结构数组(以线性时间)?
【发布时间】:2012-02-11 18:45:36
【问题描述】:

我有一个小程序计算行星相互之间的力。我的程序有两个结构数组,一个保存迭代前的位置和速度,另一个保存迭代后它们的位置和速度。

在每次迭代结束时,我想将第二个数组中的值移动到第一个数组中,而第二个数组可能会变成垃圾(但需要指向一些我以后可以写入的有效内存位置)。我想我可以简单地切换数组,因为数组是一个指针,但编译器不会让我这样做。

考虑这个例子:

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]
}

这就是我想做的。它肯定会比从 1 到 N 的 for 循环更快。

【问题讨论】:

标签: c++ pointers struct


【解决方案1】:

您应该考虑使用可以在恒定时间内交换的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
// ...

【讨论】:

  • 我当然更喜欢将我的数组放在堆栈上,因为使用大括号符号很容易初始化。我会考虑实施这个建议。
  • @eternalmatt 如果您的编译器支持 C++11,您也可以使用花括号来初始化 vector(此功能称为统一初始化):std::vector&lt;Foo&gt; bar { 23, 21, 55, 268, 31, 0, -34, 23 };。如果您仍然不想使用vector,唯一的另一种方法是使用我回答中的其他建议。
【解决方案2】:

这就是你出错的地方:

因为数组是指针

这不是真的。数组衰减到一个指针,但它们不是一回事。

但是,您可以通过实际从这些数组中获取指针来轻松地做您想做的事情。

   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);

【讨论】:

  • 注意std::array只能在线性时间内交换,与将每个元素从一个元素复制到另一个元素,从另一个元素复制到另一个元素相同
  • @SethCarnegie 我正在交换指向std::array 的指针,而不是std::array 本身。
  • 数组也不会衰减为指针。数组的名称衰减为指针的名称。内存中的实际对象仍然是一个数组
  • @LightnessRacesinOrbit 我想有人可能会认为我的措辞是这样的,例如一个数组衰减为一个指针,该指针是一个左值,但这不是我的意思。为了绝对准确,我将简单引用标准:““NT 数组”或“T 的未知边界数组”类型的左值或右值可以转换为“指向 T 的指针”类型的纯右值。结果是指向数组第一个元素的指针。” [conv.array]
【解决方案3】:

问题在于您使用的是堆栈分配的数组。

当您说Foo bar[8]; 时,您正在为当前堆栈帧中的八个Foo 结构创建空间。所以你不能把它分配给别的东西的原因是它必须以某种方式移到框架之外,逃避内存管理和它应该有的范围。

您想要一个动态分配的数组 - Foo* bar = new Foo[8];。完成后,delete[] bar;。这里的不同之处在于,现在 指针 在堆栈上,但实际内容在堆上 - 因此您可以更改指针引用的位置,而不必担心移动实际指针本身。

另一种选择是使用具有复制构造函数和operator= 的类,例如std::vectorstd::liststd::deque

顺便说一下,复制数组内容的过程是线性时间。指针赋值是常数时间 (O(1)),因为无论您的数组有多大 - 移动指针始终是一个操作。

【讨论】:

    【解决方案4】:

    你可以使用 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);
    }
    

    【讨论】:

      【解决方案5】:

      您可以将数组声明为临时数组,也可以声明指针 - 您将在函数的其余部分使用它们。

      这些指针可以轻松交换。

      int main()
      {
         Foo temp1[8], temp2[8];
         Foo *bar = temp1, *baz = temp2;
      
         Foo *temp = baz;
         baz = bar;  //no problems now 
         bar = temp;  
      }
      

      【讨论】:

        【解决方案6】:
           Foo bar[8], baz[8];
        

        然后

           Foo* arr1 = bar, arr2 = bax;
        

        忘记bazbar,只使用arr1arr2

        交换:

          int* temp = arr1;
          arr1 = arr2;
          arr2 = temp;
        

        【讨论】:

        • 不要忘记在arr2 之前将* 声明为Foo* 而不是Foo
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-08-18
        • 2017-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多