使用c風格字符串初始化std::string時存在兩種可能的錯誤情況:
- 傳入空指針,
- 傳入的c風格字符串不以'\0'結尾。
g++ (GCC) 11.2.0 中,使用c風格字符串初始化 std::string(basic_string)的代碼如下:
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc())
: _M_dataplus(_M_local_data(), __a)
{
const _CharT* __end = __s ? __s + traits_type::length(__s)
// We just need a non-null pointer here to get an exception:
: reinterpret_cast<const _CharT*>(__alignof__(_CharT));
_M_construct(__s, __end, random_access_iterator_tag());
}
第4-6行的三目運算符作用如下:如果__s是一個空指針,則將__end設置為1;否則調用下列函數(進而調用libc中的strlen。錯誤1使用不以'\0'結尾的字符串調用strlen是未定義行為(The behavior is undefined if str
is not a pointer to a null-terminated byte string.https://en.cppreference.com/w/c/string/byte/strlen)),將__end設置為滿足迭代器要求的指針位置(字符串末尾'\0'的位置)。
static _GLIBCXX17_CONSTEXPR size_t
length(const char_type* __s)
{
#if __cplusplus >= 201703L
if (__constant_string_p(__s))
return __gnu_cxx::char_traits<char_type>::length(__s);
#endif
return __builtin_strlen(__s);
}
_M_construct
進一步調用下列函數,錯誤2如果第一步傳入的__s為空指針,這里第9-11行會拋出異常.
經過一系列錯誤檢查,在17行創建內部c string空間,在23行進行字符串拷貝
template<typename _CharT, typename _Traits, typename _Alloc>
template<typename _InIterator>
void
basic_string<_CharT, _Traits, _Alloc>::
_M_construct(_InIterator __beg, _InIterator __end,
std::forward_iterator_tag)
{
// NB: Not required, but considered best practice.
if (__gnu_cxx::__is_null_pointer(__beg) && __beg != __end)
std::__throw_logic_error(__N("basic_string::"
"_M_construct null not valid"));
size_type __dnew = static_cast<size_type>(std::distance(__beg, __end));
if (__dnew > size_type(_S_local_capacity))
{
_M_data(_M_create(__dnew, size_type(0)));
_M_capacity(__dnew);
}
// Check for out_of_range and length_error exceptions.
__try
{ this->_S_copy_chars(_M_data(), __beg, __end); }
__catch(...)
{
_M_dispose();
__throw_exception_again;
}
_M_set_length(__dnew);
}