【问题标题】:How to convert a vector<vector<int>> to int** without rebuilding the array?如何在不重建数组的情况下将 vector<vector<int>> 转换为 int**?
【发布时间】:2014-01-02 01:11:33
【问题描述】:

我不喜欢过多地使用指针,我一直在使用 stl 工具。我遇到了这种情况,我需要将一个 int** 传递给一个需要使用 2d 整数数组的第三方函数。我编写了以下函数,该函数创建了一个 int*s 向量来完成这项工作。我的问题是,假设 third_party 函数不更改数组的结构并且只访问数据条目,这样做是否安全。有没有更好的方法来做到这一点?

这里是函数def:

void ConvertVectorVectorToIntStarStar(const std::vector<std::vector<int> >& v,vector<int*>* a) {
  assert(a);
  int n = v.size();
  a->resize(n);
  for(int i = 0; i < n; ++i)
    (*a)[i] = (int*)(&v[i][0]);
}

这是用法:

  vector<int*> p;
  ConvertVectorVectorToIntStarStar(v, &p);
  int** a = &p[0];
  third_party_function(a);

【问题讨论】:

  • 您可以使用v.data() 代替&amp;v[0]
  • @Jarod42:那只是在 C++11 中,问题被标记为 C++。
  • &amp;v[0]如果v.empty()是非法的,所以如果你缺少.data()你应该v.empty()?NULL:&amp;v[0],这很烦人,或者升级你的编译器,这是彩虹。
  • OP,你有 C++11 支持吗?数组是“锯齿状的”还是大小都一样?它们在运行时是否会发生变化——它们是如何结束的?
  • @Yakk 不,我没有 11。我不在乎 v 为空的情况,因为这不会发生。

标签: c++ pointers vector stl


【解决方案1】:

如何将vector&lt;vector&lt;int&gt;&gt; 转换为int**

你不能简单地转换它。

vector&lt;int&gt; 向量的内部布局与int* 数组完全不兼容。 :-)

这是你的类型:

vector<vector<int>> v{{ 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 } };
int (*a)[] = { new int[3], new int[3], new int[3] };

您首先会注意到aint (*)[] 而不是int**。当您使用a 时,它会衰减为int**int[][] 不会这样做

让我们得到我们的第一级指针:

vector<int>* ptr1 = &v[0];
int**        ptr2 = &a[0];

现在让我们看看存储:

  • ptr1[0]vector&lt;int&gt;
  • ptr2[0]int*

显然,这些都不是同一类东西。甚至不需要再进一步,内存布局已经不同了。这意味着您不能仅仅将int**“获取”到向量的数据中。


但是,如果您不介意从头开始构建最外层的“索引​​”数组,那么您的方法基本上是合理的。这不是您所追求的零步骤过程。


我的建议是用二维索引访问语义包装vector&lt;int&gt;(长度为宽度×高度),而不是所有这些。然后,您可以根据需要使用int* 对整个数据缓冲区进行操作。

感谢Freenode ##c++

template<typename T>
struct matrix
{
   matrix(unsigned m, unsigned n)
      : m(m), n(n), vs(m*n)
   {}

   T& operator()(unsigned i, unsigned j)
   {
      return vs[i + m * j];
   }

private:
   unsigned m, n;
   std::vector<T> vs;
};

/* column-major/opengl: vs[i + m * j], row-major/c++: vs[n * i + j] */

【讨论】:

  • 我已经创建了一个测试用例,看起来像这样。
  • 你不必完全重建数组,你可以将int*的数组指向向量的内容,这不就是他在做的吗?
  • @user2345215:好吧,我想如果你从头开始构建最外层的数组,那么可以,但这是一个重建步骤。
  • @MohammadMoghimi:他们是。问题不是最内在的维度,而是外在的维度。您的代码使用指向实际矢量数据的指针从头开始重建最外层维度,这很好。但是,如果您想从最外层的向量中“获取”int** 而根本不重新组织,您将无法做到这一点。
  • @MohammadMoghimi:这将取决于您是“每个外部元素有很多内部元素”还是“内部元素很少的许多外部元素”,但总的来说,如果你有千兆字节而不是拥有所有这些内部向量不会非常有效。我肯定会看一下 1D 存储上的矩阵包装器。
【解决方案2】:

如果你需要演员表,那是不对的:

(*a)[i] = (int*)(&v[i][0]);

这真的应该读

(*a)[i] = const_cast<int*>(v[i].data());

否则代码看起来没问题,它应该可以工作:std::vector&lt;T&gt; 中的元素是连续的。

【讨论】:

  • +1 只是为了对抗愚蠢的无法解释的反对票。但是,使用“否则代码看起来不错”,您忘记了零大小向量的问题。 可能是 OP 刚刚忽略的。
【解决方案3】:
std::vector<std::vector<int> > vt;
// some code with vt..

std::vector<int *> vtp;
for (const std::vector<int> &vtr : vt)
    vtp.push_back(const_cast<int *>(vtr.data()));

我还没有测试,但我认为我工作得很好

【讨论】:

    【解决方案4】:

    而不是当前的代码……

    void ConvertVectorVectorToIntStarStar(const std::vector<std::vector<int> >& v,vector<int*>* a) {
      assert(a);
      int n = v.size();
      a->resize(n);
      for(int i = 0; i < n; ++i)
        (*a)[i] = (int*)(&v[i][0]);
    }
    

    我建议你这样做……

    vector<int*> IntStarStarFrom( std::vector<std::vector<int>>& v)
    {
        int const n = static_cast<int>( v.size() );
        assert( n > 0 );
        vector<int*> a( n );
    
        for(int i = 0; i < n; ++i)
        {
            assert( v[i].size() > 0 );
            a[i] = &v[i][0];
        }
        return std::move( a );
    }
    

    免责声明:现成的代码,编译器未触及。

    断言表达了相关的安全问题。

    向量的缓冲区保证是连续的,但是 IIRC 在向量大小为 0 时索引向量是未定义的行为。

    使用代码,改写为使用建议的函数替换:

    vector<int*> p = IntStarStarFrom( v );
    int** const a = &p[0];
    third_party_function( a );
    

    或者正如Mark Ransom 在评论中所说,您现在可以在一行代码中完成此操作:

    third_party_function(IntStarStarFrom(v).data());
    

    【讨论】:

    • 您可以使用这种方法将呼叫变成单行:third_party_function(IntStarStarFrom(v).data());
    • @MarkRansom:谢谢!我什至没有想到这一点,只考虑对原始代码的影响。添加到答案...
    猜你喜欢
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 2013-03-15
    • 1970-01-01
    • 2021-09-03
    • 2019-11-29
    • 2020-12-14
    相关资源
    最近更新 更多