我正在做一些维护工作,并遇到了以下情况:
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]
请注意,即使在长度为 0 的字符串的情况下,C++11 标准也始终保证该事物可以正常工作。如果您这样做,则无法保证str.begin()
or &*str.begin()
,但对于&s[0]
标准定义operator[]
为:
返回:
*(begin() + pos)
如果pos < size()
,否则是对T
具有值类型的对象的引用charT()
;参考值不得修改
继续下去,data()
定义为:
返回:一个指针
p
,使得p + i == &operator[](i)
对于每个i
in[0,size()]
。
(注意范围两端的方括号)
注意:预标准化 C++0x 不保证&s[0]
可以使用零长度字符串(实际上,它是明确未定义的行为),并且此答案的旧版本对此进行了解释;这已在以后的标准草案中得到修复,因此答案已相应更新。
使用安全。我认为大多数答案曾经是正确的,但标准发生了变化。引用 C++11 标准basic_string 一般要求 [string.require] , 21.4.1.5 说:
basic_string 对象中的类字符对象应连续存储。也就是说,对于任何 basic_string 对象 s,标识 &*(s.begin() + n) == &*s.begin() + n 应适用于所有 n 值,使得 0 <= n < s.size ()。
在此之前,它说所有迭代器都是随机访问迭代器。这两个位都支持您的问题的使用。(此外,Stroustrup 显然在他的最新著作中使用了它;))
在 C++11 中完成此更改并非不可能。我似乎记得当时为向量添加了相同的保证,该版本还获得了非常有用的data()指针。
希望有帮助。
从技术上讲,不,因为std::string
不需要将其内容连续存储在内存中。
然而,在几乎所有的实现中(我知道的每一个实现),内容都是连续存储的,这将“工作”。
读者应该注意,这个问题是在 2009 年提出的,当时 C++03 标准是当前的出版物。此答案基于该版本的标准,其中不保证std::string
s使用连续存储。由于这个问题不是在特定平台(如 gcc)的上下文中提出的,因此我对 OP 的平台不做任何假设——特别是天气与否,它是否为.string
合法的?也许,也许不是。安全的?可能,但也可能不是。好代码?好吧,我们不要去那里......
为什么不这样做:
std::string s = str;
...或者:
std::string s(str);
...或者:
std::string s;
std::copy( &str[0], &str[strLen], std::back_inserter(s));
...或者:
std::string s;
s.assign( str, strLen );
?
这通常是不安全的,无论内部字符串序列是否连续存储在内存中。std::string
除了连续性之外,可能还有许多其他实现细节与对象如何存储受控序列有关。
一个真正的实际问题可能如下。的受控序列std::string
不需要存储为以零结尾的字符串。然而,在实践中,许多(大多数?)实现选择将内部缓冲区超大 1 并将序列存储为以零结尾的字符串,因为它简化了c_str()
方法的实现:只需返回一个指向内部缓冲区的指针就可以了.
您在问题中引用的代码不会将数据复制到内部缓冲区中以零终止。很可能它根本不知道零终止对于std::string
. 很可能它依赖于在调用之后用零填充的内部缓冲区resize
,因此由实现分配给零终止符的额外字符很方便地预设为零。所有这些都是一个实现细节,这意味着该技术依赖于一些相当脆弱的假设。
换句话说,在某些实现中,您可能必须使用strcpy
,而不是memcpy
像那样强制数据进入受控序列。而在其他一些实现中,您必须使用memcpy
而不是strcpy
.
该代码可能有效,但更多的是靠运气而不是判断,它对无法保证的实现做出了假设。我建议确定代码的有效性是无关紧要的,而它是一个毫无意义的过度复杂化,很容易简化为:
std::string s( str ) ;
或者如果分配给现有的 std::string 对象,只需:
s = str ;
然后让 std::string 自己决定如何实现结果。如果您要诉诸这种废话,那么您最好不要使用 std::string 并坚持使用,因为您正在重新引入与 C 字符串相关的所有危险。