使用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);
}