“& s [0]”是否指向std :: string中的连续字符?

时间:2009-12-31 20:22:16

标签: c++ memcpy stdstring c++03

我正在做一些维护工作,遇到类似以下的事情:

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 );

我知道使用& s [0]如果是std :: vector会安全的,但这是std :: string的安全使用吗?

6 个答案:

答案 0 :(得分:39)

在C ++ 98/03标准下,std :: string的分配不保证是连续的,但C ++ 11强制它。在实践中,我和Herb Sutter都不知道不使用连续存储的实现。

请注意,即使在0长度字符串的情况下,&s[0]始终保证可以通过C ++ 11标准工作。如果您执行了str.begin()&*str.begin(),则无法保证,但对于&s[0],标准将operator[]定义为:

  

返回*(begin() + pos) if pos < size(),否则引用T类型的对象,其值为charT();参考值不得修改

继续,data()定义为:

  

返回:指针pp + i == &operator[](i)中的每个i [0,size()]

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


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

答案 1 :(得分:7)

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

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

答案 2 :(得分:7)

使用安全。我认为大多数答案都是正确的,但标准改变了。引用C ++ 11标准, basic_string一般要求[string.require] ,21.4.1.5,说:

  

basic_string对象中的char类对象应连续存储。也就是说,对于任何basic_string   对象s,身份&amp; *(s.begin()+ n)==&amp; * s.begin()+ n应保留n的所有值,使得0   &lt; = n&lt; s.size()。

在此之前,它说所有迭代器都是随机访问迭代器。这两个位都支持您的问题的使用。 (此外,Stroustrup显然在他最新的书中使用它;))

在C ++ 11中进行此更改并非不太可能。我似乎记得为vector添加了相同的保证,它也获得了非常有用的 data()指针。

希望有所帮助。

答案 3 :(得分:2)

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

答案 4 :(得分:0)

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

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

您在问题中引用的代码没有做任何努力将零终止数据复制到内部缓冲区。很可能它根本不知道std::string的实现是否需要零终止。很可能它依赖于在调用resize之后内部缓冲区被零填充,因此实现为零终止符分配的额外字符被方便地预先设置为零。所有这些都是一个实现细节,这意味着这种技术取决于一些相当脆弱的假设。

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

答案 5 :(得分:0)

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

std::string s( str ) ;

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

s = str ;

然后让std :: string本身确定如何实现结果。如果你要采用这种废话,那么你也可以不使用std :: string并坚持使用,因为你重新引入了与C字符串相关的所有危险。