当前位置: 首页 > 知识库问答 >
问题:

使用std::string作为缓冲区有缺点吗?

云星波
2023-03-14

我最近看到我的一位同事使用std::string作为缓冲区:

std::string receive_data(const Receiver& receiver) {
  std::string buff;
  int size = receiver.size();
  if (size > 0) {
    buff.resize(size);
    const char* dst_ptr = buff.data();
    const char* src_ptr = receiver.data();
    memcpy((char*) dst_ptr, src_ptr, size);
  }
  return buff;
}

我猜这家伙想利用返回字符串的自动销毁,所以他不必担心释放分配的缓冲区。

这在我看来有点奇怪,因为根据cplusplus。com方法返回一个指向由字符串内部管理的缓冲区的常量字符*:

const char* data() const noexcept;

Memcpy ing to a const char pointer?好吧,只要我们知道自己在做什么,这没有害处,但我错过了什么吗?这危险吗?

共有3个答案

舒飞捷
2023-03-14

Memcpy-ing到const char指针?AFAIK只要我们知道我们在做什么,这并没有坏处,但这是好的行为吗?为什么?

根据C版本的不同,当前代码可能具有未定义的行为。为了避免C 14及以下版本中未定义的行为,请使用第一个元素的地址。它生成一个非常量指针:

buff.resize(size);
memcpy(&buff[0], &receiver[0], size);

我最近看到我的一个同事使用std::字符串作为缓冲区...

这在旧代码中有点常见,尤其是C 03左右。使用这样的字符串有几个优点和缺点。取决于您对代码的处理方式,std::vector可能有点贫血,有时您会使用字符串,并接受字符特征的额外开销。

例如,std::string通常比append上的std::向量更快,并且您不能从函数返回std::向量。(或者在C 98中实际上不能这样做,因为C 98要求在函数中构造向量并复制出来)。此外,std::string允许您搜索更丰富的成员函数,如find_first_offind_first_not_of。这在搜索字节数组时很方便。

我认为你真正想要/需要的是SGI的Rope类,但它从未进入STL。看起来GCC的libstdc可能会提供它。

关于这在C14及以下版本中是否合法,有一个冗长的讨论:

const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);

我肯定地知道在海湾合作委员会是不安全的。我曾经在一些自我测试中做过类似的事情,结果导致了一个错误:

std::string buff("A");
...

char* ptr = (char*)buff.data();
size_t len = buff.size();

ptr[0] ^= 1;  // tamper with byte
bool tampered = HMAC(key, ptr, len, mac);

GCC将单字节'A'放在寄存器AL中。高3字节是垃圾,因此32位寄存器是0xXXXXXX41。当我在ptr[0]取消引用时,GCC取消引用了一个垃圾地址0xXXXX41

对我来说,两个要点是,不要编写半途而废的自检,不要试图使data()成为非常量指针。

史和泰
2023-03-14

您可以通过调用适当的构造函数来完全避免手动memcpy

std::string receive_data(const Receiver& receiver) {
    return {receiver.data(), receiver.size()};
}

它甚至可以处理字符串中的0。

顺便说一句,除非内容实际上是文本,否则我更喜欢std::向量

姬俊远
2023-03-14

使用std::string作为缓冲区是不好的做法,原因有几个(没有按特定顺序列出):

  • std:string不打算用作缓冲区;您需要仔细检查类的描述,以确保没有“gotchas”阻止某些使用模式(或使它们触发未定义的行为)

此外,您的代码可能会进行两次而不是一次堆分配(取决于实现):一次是在字符串构造时,另一次是在调整大小时。但这本身并不是避免std::string的真正原因,因为可以使用@Jarod42答案中的构造来避免双重分配

 类似资料:
  • 我正在使用JOGL,但这个问题一般适用于OpenGL。似乎存在类似的问题,但它们要么针对GLSL代码,要么与复制帧缓冲区的内容有关,要么是一般建议-使用帧缓冲区对象,而不是。 我正在做一些阴影映射。如何使用帧缓冲对象将深度通道直接渲染到纹理? 能否请你贴一段初始化纹理和帧缓冲对象的代码,以及渲染场景前初始化一切的代码? 目前,我使用<code>glCopyTexSubImage2D<code>。我

  • 我读到FileWriter和BufferedWriter的区别在于FileWriter直接写入文件(逐字符),white BufferedReader使用缓冲区。如果是,为什么FileWriter有缓冲区?例如,如果我创建一个FileWriter对象,如下所示: 而且,如果我在程序结束时不刷新或关闭写入器,它将不会向文件写入任何内容。这意味着它也使用缓冲区。拜托,解释一下?

  • 我正在使用流协议编写服务器,所以我需要做一些事情,比如找到标题的结尾,复制它,然后在提升缓冲区中解析其他东西。当我发现使用字符串操作的最佳方法(在其中找到字符串,使用迭代器复制/删除等等)是std::字符串。但是我使用的是char数组缓冲区。所以我需要有两个缓冲区——char数组和std::字符串——每次我需要使用缓冲区进行操作时,我需要将char数组转换为std::字符串,完成我的工作,然后使用

  • 问题内容: 在编写用于OpenGL库的Matrix类时,我遇到了一个问题,即使用Java数组还是使用Buffer策略存储数据(JOGL为Matrix操作提供直接缓冲区复制)。为了对此进行分析,我编写了一个小型性能测试程序,该程序比较了Arrays vs Buffers和Direct Buffers上循环和批量操作的相对速度。 我想在这里与您分享我的结果(因为我发现它们很有趣)。请随时发表评论和/或

  • 我需要在std::字符串对象中使用一个已经分配的char*缓冲区(带有字符串内容)。经过一些研究,我发现这几乎是不可能的,std::字符串总是会有自己的私有数据副本。我能想到的唯一剩下的方法是使用一个自定义分配器,它将返回已经分配的char缓冲区的地址。要做到这一点,std::字符串应该只使用分配器来分配内存来保存它的字符串数据,而不是别的。是这样吗?

  • 两者都是序列化库,由谷歌开发人员开发。他们之间有什么大的区别吗?将使用协议缓冲区的代码转换为使用FlatBuffers需要大量工作吗?