duo*_*gja 68 c++ stdstring c++11
我最近看到我的一位同事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;
}
Run Code Online (Sandbox Code Playgroud)
我猜这家伙想利用返回字符串的自动销毁功能,因此他不必担心释放分配的缓冲区。
这对我来说有点奇怪,因为根据cplusplus.com,该data()方法返回const char*指向由字符串内部管理的缓冲区的指针:
const char* data() const noexcept;
Run Code Online (Sandbox Code Playgroud)
Memcpying到一个const char指针?AFAIK只要知道我们所做的事情就不会造成伤害,但是我错过了什么吗?这很危险吗?
ein*_*ica 70
std::string用作缓冲区。std::string出于种种原因(不按特定顺序列出),将其用作缓冲区是不好的做法:
std::string不打算用作缓冲区;您将需要仔细检查类的描述,以确保没有“陷阱”来防止某些使用模式(或使它们触发未定义的行为)。data()-它是const Tchar *; 因此您的代码将导致未定义的行为。(但是&(str[0]),&(str.front())或&(*(str.begin()))会起作用。)std::string对缓冲区使用s会使实现的读者感到困惑,后者假定您将对std::string字符串使用。换句话说,这样做违反了“最小惊讶原则”。std::unique_ptr would be fine for your case, or even std::vector. In C++17, you can use std::byte for the element type, too. A more sophisticated option is a class with an SSO-like feature, e.g. Boost's small_vector (thank you, @gast128, for mentioning it).std::string to conform to the C++11 standard, which in some cases (which by now are rather unlikely), you might run into some linkage or runtime issues that you wouldn't with a different type for your buffer.Also, your code may make two instead of one heap allocations (implementation dependent): Once upon string construction and another when resize()ing. But that in itself is not really a reason to avoid std::string, since you can avoid the double allocation using the construction in @Jarod42's answer.
Jar*_*d42 64
您可以memcpy通过调用适当的构造函数来完全避免使用手册:
std::string receive_data(const Receiver& receiver) {
return {receiver.data(), receiver.size()};
}
Run Code Online (Sandbox Code Playgroud)
甚至可以处理\0字符串。
顺便说一句,除非内容实际上是文本,否则我更喜欢std::vector<std::byte>(或等效)。
Memcpying到一个const char指针?AFAIK只要知道我们所做的事情就不会造成任何伤害,但这是好的行为,为什么?
当前代码可能具有未定义的行为,具体取决于C ++版本。为了避免在C ++ 14及以下版本中发生未定义的行为,请使用第一个元素的地址。它产生一个非常量指针:
buff.resize(size);
memcpy(&buff[0], &receiver[0], size);
Run Code Online (Sandbox Code Playgroud)
我最近看到我的一位同事
std::string用作缓冲...
这在较旧的代码中尤其常见,尤其是在C ++ 03左右。使用这样的字符串有很多好处和缺点。根据您对代码的处理方式,std::vector可能会有些贫乏,有时您会改用字符串并接受的额外开销char_traits。
例如,std::string通常是比std::vector在append上更快的容器,并且您不能std::vector从函数返回。(或者在C ++ 98中您实际上不能这样做,因为C ++ 98需要在函数中构造向量并将其复制出来)。此外,还std::string允许您使用各种成员函数(例如find_first_of和)进行搜索find_first_not_of。当搜索字节数组时,这很方便。
我认为您真正想要/需要的是SGI的Rope类,但它从未进入STL。看起来GCC的libstdc ++可能提供了它。
在C ++ 14及以下版本中,对此进行了长时间的讨论:
const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);
Run Code Online (Sandbox Code Playgroud)
我知道在GCC中这并不安全。我曾经在一些自测中做过这样的事情,结果导致了段错误:
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);
Run Code Online (Sandbox Code Playgroud)
GCC将单个字节'A'放入寄存器中AL。高3个字节是垃圾,因此32位寄存器是0xXXXXXX41。当我取消引用时ptr[0],GCC取消引用了垃圾地址0xXXXXXX41。
对我来说,两个要点是,不要编写半屁股的自我测试,也不要尝试制作data()非常量指针。
从C ++ 17,data可以返回非const char *。
草稿n4659在[string.accessors]中声明:
Run Code Online (Sandbox Code Playgroud)const charT* c_str() const noexcept; const charT* data() const noexcept; .... charT* data() noexcept;
该代码是不必要的,考虑到
std::string receive_data(const Receiver& receiver) {
std::string buff;
int size = receiver.size();
if (size > 0) {
buff.assign(receiver.data(), size);
}
return buff;
}
Run Code Online (Sandbox Code Playgroud)
将做完全一样。
我将在这里研究的最大优化机会是:Receiver似乎是某种支持.data()和的容器.size()。如果可以使用它,并将其作为右值引用传递,则Receiver&&可以使用move语义,而无需进行任何复制!如果有迭代器接口,则可以将其用于基于范围的构造函数或std::move()from中<algorithm>。
在C ++ 17(如Serge Ballesta等人提到的)中,std::string::data()返回指向非常量数据的指针。std::string已保证A 连续存储所有数据。
虽然不是真正的程序员的错,但是书面的代码有点散发出来的气味:当时这些黑客是必要的。今天,你至少应该改变的类型,dst_ptr从const char*到char*并删除投中的第一个参数memcpy()。您还可以为reserve()缓冲区添加多个字节,然后使用STL函数移动数据。
正如其他人提到的那样,在这里使用std::vector或std::unique_ptr将是更自然的数据结构。
| 归档时间: |
|
| 查看次数: |
5812 次 |
| 最近记录: |