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