40

我正在做一些维护工作,并遇到了以下情况:

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 的安全使用吗?

4

6 回答 6

48

在 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)对于每个iin [0,size()]

(注意范围两端的方括号)


注意:预标准化 C++0x 不保证&s[0]可以使用零长度字符串(实际上,它是明确未定义的行为),并且此答案的旧版本对此进行了解释;这已在以后的标准草案中得到修复,因此答案已相应更新。

于 2009-12-31T20:24:04.253 回答
8

使用安全。我认为大多数答案曾经是正确的,但标准发生了变化。引用 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()指针。

希望有帮助。

于 2015-01-19T12:36:39.960 回答
8

从技术上讲,不,因为std::string不需要将其内容连续存储在内存中。

然而,在几乎所有的实现中(我知道的每一个实现),内容都是连续存储的,这将“工作”。

于 2009-12-31T20:24:05.223 回答
3

读者应该注意,这个问题是在 2009 年提出的,当时 C++03 标准是当前的出版物。此答案基于该版本的标准,其中保证std::strings使用连续存储。由于这个问题不是在特定平台(如 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 );

?

于 2009-12-31T20:32:00.387 回答
2

这通常是不安全的,无论内部字符串序列是否连续存储在内存中。std::string除了连续性之外,可能还有许多其他实现细节与对象如何存储受控序列有关。

一个真正的实际问题可能如下。的受控序列std::string不需要存储为以零结尾的字符串。然而,在实践中,许多(大多数?)实现选择将内部缓冲区超大 1 并将序列存储为以零结尾的字符串,因为它简化了c_str()方法的实现:只需返回一个指向内部缓冲区的指针就可以了.

您在问题中引用的代码不会将数据复制到内部缓冲区中以零终止。很可能它根本不知道零终止对于std::string. 很可能它依赖于在调用之后用零填充的内部缓冲区resize,因此由实现分配给零终止符的额外字符很方便地预设为零。所有这些都是一个实现细节,这意味着该技术依赖于一些相当脆弱的假设。

换句话说,在某些实现中,您可能必须使用strcpy,而不是memcpy像那样强制数据进入受控序列。而在其他一些实现中,您必须使用memcpy而不是strcpy.

于 2009-12-31T20:46:42.910 回答
0

该代码可能有效,但更多的是靠运气而不是判断,它对无法保证的实现做出了假设。我建议确定代码的有效性是无关紧要的,而它是一个毫无意义的过度复杂化,很容易简化为:

std::string s( str ) ;

或者如果分配给现有的 std::string 对象,只需:

s = str ;

然后让 std::string 自己决定如何实现结果。如果您要诉诸这种废话,那么您最好不要使用 std::string 并坚持使用,因为您正在重新引入与 C 字符串相关的所有危险。

于 2009-12-31T20:47:07.123 回答