【问题标题】:Assigning 2D variable sized arrays分配二维可变大小的数组
【发布时间】:2018-10-03 17:42:50
【问题描述】:

我正在尝试将可变长度 c 字符串的二维数组存储到一个结构中,以便我可以通过网络套接字传输和重建它。

计划是在数据包的标题中设置行和列,帮助我阅读后面的可变尺寸镜头和 arr。我相信我一定是在语法上写错了指针,或者在将它们设置到结构中时需要使用某种辅助指针。

struct STORAGE {
   int rows;     // hdr
   int cols;     // hdr
   int** lens;
   const char*** arr;
}

// code
int rows = 11;
int cols = 2;

int lens[rows][cols];
const char* arr[rows][cols];
// ... fill with strings ...
// ... along with lens ...

STORAGE store;
store.rows = rows;
store.cols = cols;
store.lens = lens;
store.arr = arr;

编译这段代码时出现这些错误:

错误:从intint** 的无效转换[-fpermissive]

错误:无法在赋值中将 const char* [11][2] 转换为 `const char***'

我主要来自 Java 背景,但我确实了解指针的工作原理等。对于具有我背景的人(主要编写 java/c++ 和较少的 c)来说,这个语法只是有点偏题。有什么建议吗?

注意:我不使用更复杂的类型(如字符串、映射、向量等)的原因是我需要通过网络传输结构(即,如果堆的大小可变,则指向堆的指针将不起作用)。除非有人能提供更好的解决方案,否则它必须是低级数组。

【问题讨论】:

标签: c++ c++11


【解决方案1】:

除非有人能提供更好的解决方案,否则它必须是低级数组。

一维的std::vector<int>std::vector<uint8_t> 已经为您提供了一个使用std::vector::data() 成员连续分配的低级 数组。

您需要的任何其他维度都可以通过正确分割该数据来确定。对于网络传输,您需要预先提供必要的切片尺寸,然后再发送数据。

类似:

Transmit num_of_dimensions
Transmit dim_size_1, dim_size_2, dim_size_3, ...
Transmit data

Receive num_of_dimensions
Loop Receiving dimension sizes
Receive dim_size_1 * dim_size_2 * dim_size_3 * ... of data

我可能不得不处理这样的情况是一个类/结构看起来像:

template<typename T>
class MultiDimensional {
    size_t num_dimensions_; // If known in advance can be made a template parameter also
    std::vector<size_t> dimension_sizes_;
    std::vector<T> data_;
public:
    const T& indexing_accessor(...) const;
    T& indexing_accessor(...);
    std::vector<uint8_t> render_transmision_data();
    // construct from transmission data
    MultiDimensional(std::vector<uint8_t>& transmission_data); 
};

【讨论】:

  • 这打破了我必须将字节从套接字直接读取到我的结构中的惯例。使用向量是行不通的,因为它们的增长超过了存储在其中的数据。此外,C++ 字符串不会起作用,因为它们指向堆并且还会增长。
  • @ComputerEngineer88 如果您已经正确使用htonl() ntohl() 功能以使这些数字网络透明,我很确定这样的事情在任何网络环境中都可以正常工作。您自己构建的约定可能不是该问题的最佳解决方案。无论如何,您的多维数组方法不会很好地工作。
  • 我最终使用了类似于您的发送/接收命令的东西。大小,数据,大小,数据,并使用了结构内部内存不断增长的更高级别的对象。只要我以相同的方式序列化和反序列化,它就可以工作。感谢您的意见。
  • @ComputerEngineer88 哇,三个月后。但是很好;)
  • 3 个月的工作代码。在到期时给予信用:)
【解决方案2】:

使用像数组这样的低级东西对你没有多大帮助,它已经太复杂了。此外,它还会让您陷入兼容性问题(比如考虑字节顺序)。

除非您有非常严格的性能限制,否则请改用专为网络设计的解决方案:protocol buffers。这对您的情况来说有点矫枉过正,但如果您需要添加任何内容,它可以很好地扩展。

要使用协议缓冲区,首先在 .proto 文件中定义“消息”(结构),然后使用 Proto Compiler 将它们编译为 C++。

您这样定义您的消息(这是一个完整的.proto 文件):

syntax = "proto2";

package test;

message Storage {
   message Row {
       repeated string foo = 1;
   }
   repeated Row row = 1;
}

没有直接支持二维数组,但是数组数组就可以了(repeated 表示给定字段中可以有多个值,它基本上是一个向量)。如果您需要快速访问它们,您可以添加数组大小的字段,但在大多数实际情况下检查重复字段的大小就足够了。

你得到的是一个类,它拥有你需要的所有字段,负责内存管理,并拥有a bunch of methods to serialize and deserialize

C++ 代码在某些地方会稍长一些,因为您需要使用 getter 和 setter,但它应该被您永远不需要考虑序列化这一事实很好地抵消 - 它会自行发生。

这个东西在 C++ 中的使用示例如下:

#include "test.pb.h"  // Generated from test.proto

using ::test::Storage;

int main() {
  Storage s;
  Storage::Row* row1 = s.add_row();
  row1->add_foo("foo 0,0");
  row1->add_foo("foo 0,1");
  Storage::Row* row2 = s.add_row();
  row2->add_foo("foo 1,0");
  row2->add_foo("foo 1,1");

  assert(s.row_size() == 2);
  assert(s.row(0).foo_size() == 2);

  s.PrintDebugString();  // prints to stdout
}

在结果中,你会得到这个输出(注意这是调试输出,不是真正的序列化):

row {
  foo: "foo 0,0"
  foo: "foo 0,1"
}
row {
  foo: "foo 1,0"
  foo: "foo 1,1"
}

为了完整性:在上面的例子中,源文件是test.prototest.cpp,编译使用:

protoc --cpp_out=. test.proto
g++ test.cpp test.pb.cc -o test -lprotobuf

【讨论】:

  • Frax,我考虑过使用 Google 的协议缓冲区,但我的应用程序需要尽可能精简和原生,因为它运行在嵌入式系统上。这并不能解决我的问题,但这是我已经讨论过的问题。
  • 在这种情况下,我想你可以看看nanopb(除非你也考虑过)。我从未使用过它,但它似乎值得一试。
猜你喜欢
  • 1970-01-01
  • 2012-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多