对于std :: string使用operator []是否安全

时间:2015-05-12 10:17:48

标签: c++ language-lawyer

我正在使用旧的C风格界面进行战斗。我有一个带有这样签名的函数:

 /// if return_value == NULL only the length is returned
 void f( char * return_value, size_t * size_only_known_at_runtime); 

我的问题是,以下代码是否安全?

std::size required;
f( NULL, &required );
std::string s;
s.resize(required);
f( &s[0], &required );

有没有更好的方法将数据输入字符串?

2 个答案:

答案 0 :(得分:7)

是的,它是安全的,至少从C ++ 11中是明确的。来自[string.require],强调我的:

  

basic_string对象中的类似char的对象应连续存储。也就是说,对于任何basic_string   对象s,&*(s.begin() + n) == &*s.begin() + n的所有值都应保留n 0 <= n < s.size()

这是DR 530的决议。在C ++ 11之前,这在标准中并不明确,尽管无论如何它都是在实践中完成的。

在C ++ 14中,此要求已移至[basic.string]:

  

basic_string是一个连续的容器(23.2.1)。

其中[container.requirements.general]:

  

连续容器是一个支持随机访问迭代器(24.2.7)及其成员的容器   类型iteratorconst_iterator是连续的迭代器(24.2.1)。

其中[iterator.requirements.general]:

  

进一步满足对于整数值n和可解除引用的迭代器值的要求的迭代器   a(a + n)*(a + n)相当于*(addressof(*a) + n),称为连续迭代器

答案 1 :(得分:1)

问题是代码是否

std::size_t required_size;
f( nullptr, &required_size );
std::string s;
s.resize( required_size );
f( &s[0], &required_size );

是安全的。

这取决于假设哪个C ++标准,但由于在C ++ 03和C ++ 98中required_size = 0的情况下它是未定义的行为,所以一般答案是,一般来说都不安全。

在C ++ 03 std::string没有正式保证有一个连续的缓冲区,但实际上所有现存的实现都有连续的缓冲区。无论如何,现在在C ++ 11之后,连续缓冲区保证正式合并到标准中,将不会出现任何具有非连续缓冲区的新C ++ 03实现。因此,这不是问题。

问题在于C ++ 03 std::basic_string::operator[]定义如下:

C ++03§21.3.4/ 1
  

返回:如果pos < size(),则返回data()[pos]。否则,如果pos == size()const版本将返回charT()。否则,行为未定义。

因此,对于大小为0的非const字符串s,在C ++ 03中使用Undefined Behavior™来进行索引s[0]

在C ++ 11中,相应的段落§21.4.3/ 2表示结果是*(begin() + pos) if pos < size(),否则引用类型为T的对象价值charT();参考值不得修改。“

无论编译器实现哪种C ++标准,这里的代码都有效:

std::size_t required_size;
f( NULL, &required_size );    // Establish required buffer size.
if( required_size > 0 )
{
    std::string s( required_size, '#' );
    f( &s[0], &required_size );
    s.resize( strlen( &s[0] ) );
}