我正在进行一些维护工作,遇到了类似以下的内容:
std::string s;
s.resize( strLength );
// strLength is a size_t with the length of a C string in it.
memcpy( &s[0], str, strLength );
我知道如果是std::vector,使用&s[0]会安全,但是在std::string中这样使用是否安全?
在 C++98/03 标准下,std::string 的分配不保证是连续的,但 C++11 强制要求它是连续的。实际上,我和Herb Sutter都不知道有哪个实现不使用连续存储。
请注意,&s[0]
总是被 C++11 标准保证可以工作,即使是 0 长度字符串的情况也是如此。如果你使用 str.begin()
或者 &*str.begin()
是不能保证的,但对于 &s[0]
,标准定义了 operator[]
如下:
返回值:如果
pos < size()
,则返回*(begin() + pos)
,否则返回类型为T
的对象引用,其值为charT()
;所引用的值不得被修改
接着,data()
的定义如下:
返回值:一个指针
p
,满足对于每个i
在[0,size()]
中,都有p + i == &operator[](i)
。
(注意范围两端的方括号)
注意:C++0x标准化之前并不保证&s[0]
能够处理长度为零的字符串(实际上,它明确是未定义的行为),此问题在后来的标准草案中得到了解决,因此本答案已进行了更新。
s[s.length()]
的空指针不必连续。 对于所有满足0 <= n < s.length() - 1
的n,必须满足&s[n] + 1 == &s[n + 1]
。该要求隐藏在21.3.4/1中,即s[n]
必须返回与s.data()[n]
(对于n < length())相同的对象,而data()必须是连续的。 - Roger Patepos < size()
,则返回*(begin() + pos)
,否则返回一个类型为T
且值为charT()
的对象的引用;所引用的值不得被修改。”因此,使用&str[0]
始终是安全的。 - Matteo Italia一个basic_string对象中的字符对象必须被连续存储。也就是说,对于任何basic_string对象s,当0<= n < s.size()时,&*(s.begin() + n) == &*s.begin() + n应该成立。
在此之前,它说所有迭代器都是随机访问迭代器。这两个部分都支持你所问的用法。(另外,Stroustrup显然在他的最新书中使用了它;))
很可能这个变化是在C++11中完成的。我似乎记得同样的保证也是在那个版本中添加给向量的,它还获得了非常有用的data()指针。
希望对你有所帮助。
从技术上讲,不是的,因为并不要求 std::string
在内存中连续存储其内容。
然而,在几乎所有实现中(我所知道的每个实现),内容都是连续存储的,这样做是可行的。
string
不需要在内存中连续存储其内容(即在 C++11 之前必须要求的)。如果字符串是不连续的,那么 data
和 c_str
是否无法在常数时间内实现呢? - user541686data
或c_str
具有常数时间复杂度。此外,C++03 §21.3/5指出,data
和c_str
都可能使序列中的迭代器、引用和指针失效。 - James McNellis合法吗?也许,也许不是。安全吗?可能是,但也可能不是。好的代码?好吧,我们不去那里......
为什么不直接这样做:
std::string s = str;
...or:
std::string s(str);
...或:
std::string s;
std::copy( &str[0], &str[strLen], std::back_inserter(s));
...或者:
std::string s;
s.assign( str, strLen );
?
resize
后,您可以相当确定内部字符串是否符合实现要求的空终止。最终,在调用 resize
后,您必须拥有一个有效的 n 字符串(根据需要填充零字符)。- 然而,这表明对 std::string
类的理解不足:memcpy 被使用时要么是出于无知,要么是出于错误的性能优化尝试(由于 resize
调用,代码最终将两次向缓冲区分配值)。 - UncleBensresize
调用用零填充字符串。但是,标准仅保证填充到请求的大小(在这种情况下为strLength
),但对于额外字符,如果实现分配了一个,则标准没有保证。 - AnT stands with Russiadata()
和c_str()
都必须返回相同的缓冲区,并且c_str()
必须始终返回指向以空字符结尾的缓冲区的指针(当为空时,data()
允许返回nullptr
)。在C++11之前,内部缓冲区不必须以空字符结尾(甚至不连续),但大多数实现都是这样做的,因为它简化了c_str()
的实现。 - Remy Lebeaustd::string s( str ) ;
或者如果要分配给现有的std::string对象,只需:
s = str ;
然后让std::string本身确定如何实现结果。如果你要诉诸这种无稽之谈,那么你可能就不应该使用std::string,而应该坚持使用C字符串,因为你重新引入了与C字符串相关的所有危险。
std::string s (str, strLen);
- GManNickG
memset
和memcpy
,并学会其中的原因。这是一个值得您积累经验的方面。 - Thomas Matthews