【问题标题】:Using read() directly into a C++ std:vector将 read() 直接用于 C++ std:vector
【发布时间】:2010-05-06 10:36:24
【问题描述】:

我正在一些 C++ 中为嵌入式系统封装用户空间 linux 套接字功能(是的,这可能又是在重新发明轮子)。

我想提供一个使用向量的读写实现。

写很容易,我可以通过&myvec[0] 避免不必要的复制。我想做同样的事情并直接读入向量,而不是读入字符缓冲区然后将所有内容复制到新创建的向量中。

现在,我知道我要读取多少数据,并且可以适当分配 (vec.reserve())。我也可以阅读&myvec[0],尽管这可能是一个非常糟糕的想法。显然这样做不允许 myvec.size 返回任何合理的东西。有没有办法做到这一点:

  1. 从安全/C++ 的角度来看并不完全令人讨厌
  2. 不涉及数据块的两个副本 - 一次从内核到用户空间,一次从 C char * 样式缓冲区到 C++ 向量。

【问题讨论】:

  • 一个字符向量(嗯,字节)!
  • 您可能希望使用uint8_tunsigned char 作为字节。
  • @tzaman: read 采用char*,因此无论您如何修饰它,如果您想要保证无符号数据,您必须在某个时候转换或重新解释。在带有签名字符的非 2 补码系统上,重新解释(通过使用 vector<unsigned char>)将是灾难性的。在更明智的系统上,这只是一个问题,即您是要强制转换指针,还是在将值从向量中取出时隐式转换。

标签: c++ sockets vector buffer


【解决方案1】:

使用resize() 而不是reserve()。这将正确设置向量的大小——之后,&myvec[0] 像往常一样保证指向一个连续的内存块。

编辑:使用&myvec[0] 作为指向底层数组的指针以进行读写是安全的,并且保证符合 C++ 标准。这是 Herb Sutter has to say:

那么为什么人们不断地询问 std::vector(或 std::array)的元素是否连续存储?最可能的原因是他们想知道是否可以提供指向内部的指针以与处理 C 数组的其他代码共享数据,无论是读取还是写入。这是一种有效的用途,而且它的重要性足以在标准中得到保证。

【讨论】:

  • 这很好,并且肯定回答了第 2 点(经过测试并且也有效,谢谢!:-)),但是这种方法安全干净,还是有其他方法?不禁觉得把vectors连续内存块的地址传进去写好像很危险?
  • @Joe:用 Herb Sutter 的话来说:“不要畏缩”——它非常安全。请参阅我上面的编辑以及 Herb Sutter 博客条目的链接。
  • 太棒了!谢谢 :-) stackoverflow.com 一如既往地提供!
  • 我是一个来自未来的人,遇到了几乎完全相同的问题。我正在考虑“.resize(...)”,但它初始化了值(这对性能不利)。似乎它仍然是最好的选择。 +1。
【解决方案2】:

我将添加一个简短的说明,因为答案已经给出。参数大于当前大小的 resize() 会将元素添加到集合中并默认 - 初始化它们。如果你创造了

std::vector<unsigned char> v;

然后调整大小

v.resize(someSize);

所有无符号字符都将被初始化为 0。顺便说一句,您可以对构造函数做同样的事情

std::vector<unsigned char> v(someSize);

所以理论上它可能比原始数组慢一点,但如果替代方案是无论如何都要复制数组,那就更好了。

Reserve 仅准备内存,因此如果新元素添加到集合中,则无需重新分配,但您无法访问该内存。

您必须获取有关写入向量的元素数量的信息。 vector 对此一无所知。

【讨论】:

    【解决方案3】:

    假设它是一个 POD 结构,请调用 resize 而不是 reserve。如果您真的不希望在填充向量之前将数据清零,则可以定义一个空的默认构造函数。

    它有点低级,但是 POD 结构的构造语义是故意模糊的。如果允许memmove 复制构造它们,我不明白为什么不应该使用套接字读取。

    编辑:啊,字节,不是结构。好吧,您可以使用相同的技巧,并仅使用 char 和忽略初始化它的默认构造函数定义一个结构......首先是resize

    【讨论】:

      【解决方案4】:

      如果您希望向量反映读取的数据量,请调用resize() 两次。阅读前一次,给自己阅读的空间。再次读取后,将向量的大小设置为实际读取的字节数。 reserve() 不好,因为调用 reserve 不会授予您访问为容量分配的内存的权限。

      第一个resize() 会将向量的元素归零,但这不太可能产生很大的性能开销。如果是这样,那么您可以尝试 Potatoswatter 的建议,或者您可以放弃反映所读取数据大小的向量大小,而只需 resize() 一次,然后像分配缓冲区一样重新使用它在 C 中。

      就性能而言,如果您在用户模式下从套接字读取数据,很可能您可以轻松处理数据,只要数据进入就可以。如果您要连接到千兆位 LAN 上的另一台机器,或者如果您的机器经常运行 100% 的 CPU 或 100% 的内存带宽。如果您最终还是要阻止 read 调用,那么一点额外的复制或 memsetting 没什么大不了的。

      和你一样,我想避免用户空间中的额外副本,但不是出于性能原因,只是因为如果我不这样做,我就不必为它编写代码......

      【讨论】:

      • 通常我不会真正关心额外的副本,但我使用的是资源不足的实时系统。整个读/写过程是非阻塞的。当然,这显然会导致关于实时系统中的 STL 容器与静态内存分配等的问题......
      猜你喜欢
      • 2014-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-06
      • 2021-09-11
      • 1970-01-01
      相关资源
      最近更新 更多