对 std::string 使用运算符 [] 是否安全
Is it safe to use operator [] for std::string
我正在与旧的 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 );
是否有更好的方法将数据放入字符串中?
是的,它是安全的,至少在 C++11 中是这样。来自 [string.require],强调我的:
The char-like objects in a basic_string
object shall be stored contiguously. That is, for any basic_string
object s, the identity &*(s.begin() + n) == &*s.begin() + n
shall hold for all values of n
such that 0 <= n < s.size()
.
这是 DR 530 的决议。在 C++11 之前,这在标准中并不明确,尽管它在实践中已经完成。
在 C++14 中,此要求已移至 [basic.string]:
A basic_string
is a contiguous container (23.2.1).
其中 [container.requirements.general]:
A contiguous container is a container that supports random access iterators (24.2.7) and whose member
types iterator
and const_iterator
are contiguous iterators (24.2.1).
其中 [iterator.requirements.general]:
Iterators that further satisfy the requirement that, for integral values n
and dereferenceable iterator values
a
and (a + n)
, *(a + n)
is equivalent to *(addressof(*a) + n)
, are called contiguous iterators.
问题是代码是否
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:
” Returns: If pos < size()
, returns data()[pos]
. Otherwise, if pos == size()
, the const
version returns charT()
. Otherwise, the behavior is undefined.
因此,对于大小为 0 的非 const
字符串 s
,在 C++03 中,Undefined Behavior™ 进行索引 s[0]
.
在 C++11 中,相应的段落 §21.4.3/2 说结果是“*(begin() + pos)
if pos < size()
,否则引用类型为 [=25= 的对象” ] 的值为 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] ) );
}
我正在与旧的 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 );
是否有更好的方法将数据放入字符串中?
是的,它是安全的,至少在 C++11 中是这样。来自 [string.require],强调我的:
The char-like objects in a
basic_string
object shall be stored contiguously. That is, for any basic_string object s, the identity&*(s.begin() + n) == &*s.begin() + n
shall hold for all values ofn
such that0 <= n < s.size()
.
这是 DR 530 的决议。在 C++11 之前,这在标准中并不明确,尽管它在实践中已经完成。
在 C++14 中,此要求已移至 [basic.string]:
A
basic_string
is a contiguous container (23.2.1).
其中 [container.requirements.general]:
A contiguous container is a container that supports random access iterators (24.2.7) and whose member types
iterator
andconst_iterator
are contiguous iterators (24.2.1).
其中 [iterator.requirements.general]:
Iterators that further satisfy the requirement that, for integral values
n
and dereferenceable iterator valuesa
and(a + n)
,*(a + n)
is equivalent to*(addressof(*a) + n)
, are called contiguous iterators.
问题是代码是否
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[]
定义如下:
” Returns: If
pos < size()
, returnsdata()[pos]
. Otherwise, ifpos == size()
, theconst
version returnscharT()
. Otherwise, the behavior is undefined.
因此,对于大小为 0 的非 const
字符串 s
,在 C++03 中,Undefined Behavior™ 进行索引 s[0]
.
在 C++11 中,相应的段落 §21.4.3/2 说结果是“*(begin() + pos)
if pos < size()
,否则引用类型为 [=25= 的对象” ] 的值为 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] ) );
}