【问题标题】:C++: Getting T** from vector<vector<T>>C++:从vector<vector<T>>获取T**
【发布时间】:2017-10-24 01:15:04
【问题描述】:

我正在与需要float** 类型的二维数组的 C 库交互,其大小在运行时确定。我想使用诸如 std::vector 之类的 STL 容器来管理此内存,但 vector&lt;vector&lt;float&gt;&gt;::data() 提供 vector&lt;float&gt;*,而不是 float**。我能做什么?

【问题讨论】:

  • 分配您自己的float**vector&lt;float*&gt; 并用指向内部向量data() 的指针填充它。您不能免费获得float** - 它只是不存在于您选择的数据结构中。
  • 请问为什么我的问题被否决了?我非常努力地简洁地提出这个问题,并在发布之前进行了广泛的搜索。我怎么能做得更好?公认的答案很有趣且有见地。不明白为什么这篇文章不受欢迎......

标签: c++ stl


【解决方案1】:

您可以创建一个新的 vector,其中包含指向由您的 vector of vectors 的内部 vector 管理的所有内部数组的指针:

void old_c_function(float** floats, std::size_t X, std::size_t Y)
{
    for(auto x = 0U; x < X; ++x)
        for(auto y = 0U; y < Y; ++y)
            std::cout << "[" << x << ", " << y << "] = " << floats[x][y] << '\n';
}

int main()
{
    std::vector<std::vector<float>> v =
    {
        {1.2, 3.4, 5.6},
        {7.8, 9.0, 1.2},
    };

    // create a new vector to hold the pointers to the arrays
    // managed by the internal vectors
    std::vector<float*> v_ptrs;
    v_ptrs.reserve(v.size());

    // add the addresses of all the arrays to the new vector
    std::for_each(std::begin(v), std::end(v),
        [&v_ptrs](auto& v){ v_ptrs.push_back(v.data()); });

    // call your legacy function using your pointer vector
    old_c_function(v_ptrs.data(), v.size(), v.front().size());
}

输出:

[0, 0] = 1.2
[0, 1] = 3.4
[0, 2] = 5.6
[1, 0] = 7.8
[1, 1] = 9
[1, 2] = 1.2

注意:

显然,如果您更改向量,则需要重建指针向量,因为地址可能会发生变化。

您可以通过一些包装函数即时重建它,如下所示:

void new_wrapper_function(std::vector<std::vector<float>>& v)
{
    // create a new vector to hold the pointers to the arrays
    // managed by the internal vectors
    std::vector<float*> v_ptrs;
    v_ptrs.reserve(v.size());

    // add the addresses of all the arrays to the new vector
    std::for_each(std::begin(v), std::end(v)
        [&v_ptrs](auto& v){ v_ptrs.push_back(v.data()); });

    // call your legacy function using your pointer vector
    old_c_function(v_ptrs.data(), v.size(), v.front().size());
}

或者(我最喜欢的)构建一个包装器 class 来封装两个向量,并在主向量在其一个维度上增加容量时更新指针向量。

【讨论】:

  • ... 然后您希望 C 函数只是有一个草率的声明,实际上将其参数视为 float* const*
  • @aschepler C 函数可以毫无问题地更新数组,指针指向原始数组中的实际数据。
  • std::copystd::back_inserter(v) 怎么样?
  • @aschepler 因为我没有复制float 值,所以我正在获取每个内部数组的第一个元素的地址。
  • 哦,呵呵。 std::vector&lt;float&gt; 不会很好地初始化 float*。没有。
【解决方案2】:

我会说使用

typedef std::pair<int, int> shape;
std::pair<float **, shape> obj;

std::tuple<float **, int, int> obj;

随你喜欢。

【讨论】:

    【解决方案3】:

    我能做什么?

    a) 使用一维向量

     typedef std::vector<float> FloatVec_t
    

    b) 要访问一维向量,您可以从二维坐标计算一维索引,这是一个可以优化掉的简单函数。

     // size_t maxCol; is runtime size info
    
     size_t indx1D(size_t r, size_t c) 
     { return static_cast<size_t>((r*maxCol) + c); }
    

    c) 创建/使用 getElement(x,y) 进行二维访问:

     FloatVec_t  fltVec1D;  
    
     // 2d access
     float* getElement(size_t r, size_t c) { return (fltVec [indx1D (r,c)]); }
    

    d) 在一维向量之上创建 arr。请参阅下面的工作代码。

    环境:

    Ubuntu 15.10; g++ 版本 5.2.1 20151028;旧版戴尔速龙

    代码/MCVE:

    #include "../../bag/src/dtb_chrono.hh"
    #include <iostream>
    #include <iomanip>
    #include <vector>
    #include <cassert>
    
    typedef std::vector<float> FloatVec_t;
    
    class T537_t
    {
    private:
       FloatVec_t fltVec1D;
       size_t     maxCol;
       size_t     maxRow;
    
    public:
       T537_t () = default;
       ~T537_t() = default;
    
       int exec(size_t a_MaxCol,
                size_t a_MaxRow)
          {
             maxCol = a_MaxCol;
             maxRow = a_MaxRow;
    
             size_t N = maxCol * maxRow;
    
             std::cout << "\n  sizeof(float): " << sizeof(float) << std::endl;
    
             std::cout << "\n  Col x Row: " << maxCol << " x " << maxRow
                       << " = " << N
    
                       << "\n\n  reserved " << N << " elements in 1D vec" << std::endl;
    
             fltVec1D.reserve(N);
    
             std::cout << "\n  initialize 1D vec with values computed from row and col" << std::flush;
    
             for (uint r=1; r<=maxRow; ++r)
                for (uint c=1; c<=maxCol; ++c)
                   fltVec1D.push_back (100.1F * static_cast<float> (((r-1)*maxCol) + c));
    
             std::cout << "\n\n  report fltVec1D addrs: " << std::flush;
             for (uint r=0; r<maxRow; ++r)
             {
                std::cout << "\n    fltVec1D[" << std::setw(2) << r << "]  " << std::flush;
                for (uint c=0; c<maxCol; ++c)
                   std::cout << "  " << std::setw(8)
                             << &fltVec1D[indx1D(r,c)] << std::flush;
                std::cout << std::flush;
             }
    
             std::cout << "\n\n  report fltVec1D data: " << std::flush;
             for (uint r=0; r<maxRow; ++r)
             {
                std::cout << "\n    fltVec1D[" << std::setw(2) << r << "]  " << std::flush;
                for (uint c=0; c<maxCol; ++c)
                   std::cout << "  " << std::setw(8) << std::setprecision(5)
                             << fltVec1D[indx1D(r,c)] << std::flush;
                std::cout << std::flush;
             }
    
             // overlay arr on top of fltVec1D data
             float** arr   = new float* [maxRow]; // arr contains rows
             {
                for (size_t r=0; r < maxRow; ++r)
                   arr[r] = getElement(r,0);      // rows already exist
             }                                    // and start at col 0,
    
    
             std::cout << "\n\n  report arr data: " << std::flush;
             for (size_t r=0; r<maxRow; ++r)
             {
                std::cout << "\n         arr[" << std::setw(2) << r << "]  " << std::flush;
                for (size_t c=0; c<maxCol; ++c)
                {
                   std::cout << "  " << std::setw(8) << std::setprecision(5)
                             << arr[r][c] << std::flush;
                }
                std::cout << std::flush;
             }
    
             std::cout << "\n\n\n  report address diffs:   &arr[r][c] - getElement(r,c) : \n" << std::flush;
             for (uint r=0; r<maxRow; ++r)
             {
                std::cout << "\n          row" << std::setw(2) << r << "   " << std::flush;
                for (uint c=0; c<maxCol; ++c)
                {
                   float* addr1 = & arr[r][c];
                   float* addr2 = getElement(r,c);
                   std::cout << "  " << std::setw(8) << (addr1 - addr2) << std::flush;
                }
                std::cout << std::flush;
             }
    
             delete[] arr; // delete of  float** arr   = new float* [maxCol];
    
             return 0;
          }
    
    private: // methods
    
       size_t indx1D(size_t r, size_t c)
          {
             assert(r<maxRow);
             assert(c<maxCol);
             return static_cast<size_t>((r*maxCol) + c);
          }
    
       float* getElement(size_t r, size_t c) { return (& fltVec1D [indx1D(r,c) ] ); }
    
    }; // class T537_t
    
    
    int main(int argc, char* argv[])
    {
       if(argc > 1)
       {
          std::cout << "\nargc: " << argc << std::endl;
          for (int i = 0; i < argc; i += 1) std::cout << argv[i] << " ";
          std::cout << std::endl;
       }
    
       if(3 != argc) {
          std::cerr << "\n  2 Required parameters: maxCol maxRow " << std::endl;
          return (0);
       }
    
       setlocale(LC_ALL, "");
       std::ios::sync_with_stdio(false);
    
       int retVal = -1;
       {
          T537_t   t537;
    
          Time_t start_us = HRClk_t::now();
    
          retVal = t537.exec(static_cast<size_t>(std::atoi(argv[1])),   // col - i.e. 5
                             static_cast<size_t>(std::atoi(argv[2])));  // row - i.e. 20
    
          auto  duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);
    
          std::cout << "\n\n  t537.exec() duration  " << duration_us.count() << " us" << std::endl;
       }
    
       return(retVal);
    }
    

    命令行参数的输出示例 3 5

      sizeof(float): 4
    
      Col x Row: 3 x 5 = 15
    
      reserved 15 elements in 1D vec
    
      initialize 1D vec with values computed from row and col
    
      report fltVec1D addrs: 
        fltVec1D[ 0]    0x179dc50  0x179dc54  0x179dc58
        fltVec1D[ 1]    0x179dc5c  0x179dc60  0x179dc64
        fltVec1D[ 2]    0x179dc68  0x179dc6c  0x179dc70
        fltVec1D[ 3]    0x179dc74  0x179dc78  0x179dc7c
        fltVec1D[ 4]    0x179dc80  0x179dc84  0x179dc88
    
      report fltVec1D data: 
        fltVec1D[ 0]       100.1     200.2     300.3
        fltVec1D[ 1]       400.4     500.5     600.6
        fltVec1D[ 2]       700.7     800.8     900.9
        fltVec1D[ 3]        1001    1101.1    1201.2
        fltVec1D[ 4]      1301.3    1401.4    1501.5
    
      report arr data: 
             arr[ 0]       100.1     200.2     300.3
             arr[ 1]       400.4     500.5     600.6
             arr[ 2]       700.7     800.8     900.9
             arr[ 3]        1001    1101.1    1201.2
             arr[ 4]      1301.3    1401.4    1501.5
    
    
      report address diffs:   &arr[r][c] - getElement(r,c) : 
    
              row 0            0         0         0
              row 1            0         0         0
              row 2            0         0         0
              row 3            0         0         0
              row 4            0         0         0
    

    总结:

    本单元测试代码演示:

    a) 构建并初始化一维 std::vector。

    b) 使用内置数组构造和 new 创建“float** arr”。

    c) 快速 for 循环使用 getElement(r,0) 加载每个行指针。

    d) 单元测试显示来自 1d vec (fltVec1D) 和 arr 的相同信息(地址和数据)

    e) 测试通过比较地址来结束,这表明这两个名称提供了对相同地址和相同数据的访问。

    “arr”适用于交付给遗留功能(未测试)。

    【讨论】:

    • 这如何帮助将某些内容传递给需要 float** 的现有函数?
    • 通常当函数文档声称 float** p 表示一个 mxn 二维数组时,这意味着元素是 p[i][j] 其中 i&lt;m &amp;&amp; j&lt;n,而不是 *p[i*n+j]。 (是的,所以这里不涉及数组数组。不过。)
    • @aschepler - 感谢您的问题和评论.. 我决定提供一些代码,并且不得不寻找它,然后修改它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-18
    • 1970-01-01
    相关资源
    最近更新 更多