std::string str = NULL; 为什么会导致coredump
在C++中,std::string
是一个类,用于表示字符串。当您试图将NULL
分配给std::string
对象时,会引发未定义行为,因为std::string
不应该接受NULL
作为其值。在这种情况下,未定义行为可能导致coredump,因为您试图将一个空指针赋值给std::string
对象。
如果您想要创建一个空的std::string
,您可以简单地使用如下方法:
std::string str; // 创建一个空的 std::string
或者,如果您想要创建一个包含特定内容的std::string
,您可以这样做:
std::string str("some content");
总之,避免将NULL
分配给std::string
对象,以避免未定义行为和潜在的coredump。
为什么编译可以通过
我之前的回答可能有误,导致您的困惑。实际上,将NULL
分配给std::string
在某些编译器和情况下可能会通过编译。原因是NULL
在C++中通常定义为整数零,而不是一个真正的空指针。因此,将NULL
分配给std::string
时,编译器可能会尝试将整数零转换为std::string
。
然而,即使编译通过,这种用法仍然是不正确的,并可能导致运行时错误,如coredump。这是因为std::string
并不期望接收整数值作为其构造函数的参数,尤其是一个零值。
为了避免这种问题,您应该遵循正确的初始化方法,如我之前所示。在C++11及更高版本中,您还可以使用nullptr
来表示真正的空指针,而不是使用NULL
。这将有助于避免类型混淆和相关的问题。
总之,虽然在某些情况下,将NULL
分配给std::string
可能会通过编译,但这仍然是错误的用法,可能导致运行时错误。您应该遵循正确的std::string
初始化方法,以避免此类问题。
运行时抛出了std::length_error,为什么
首先,我为之前的解释道歉,让我们重新审视问题。当你尝试将 NULL
分配给 std::string
时,这将导致 std::length_error
异常。这是因为在这种情况下,std::string
的构造函数试图用一个空指针(const char*
类型)来初始化字符串,这是不允许的。
std::string
的构造函数期望接收一个以空字符结尾的字符数组。当你传递 NULL
时,它被解释为一个无效的 const char*
。当构造函数试图计算字符数组的长度时,它将遇到无法访问的内存区域,从而抛出 std::length_error
异常。
要避免这个问题,如果你想创建一个空字符串,只需使用以下方法之一:
std::string str; // 创建一个空的 std::string
或者,如果您想要创建一个包含特定内容的std::string
,您可以这样做:
std::string str("some content");
总之,避免将 NULL
分配给 std::string
对象,以免引发 std::length_error
异常。
std::string做出这种检查并抛出异常的代码在哪里?
在C++标准库的实现中,std::string
的构造函数会检查传入的const char*
参数是否有效。如果传入了一个空指针,那么构造函数可能会抛出一个std::length_error
异常或其他异常,具体取决于实现。
请注意,C++标准库的具体实现可能因编译器和平台而异。因此,无法提供确切的源代码位置。然而,我可以给您一个关于如何检查const char*
指针的有效性的简化示例。
假设我们有一个名为MyString
的简化的std::string
实现,代码如下:
#include <stdexcept> #include <cstring> class MyString { public: MyString(const char* s) { if (s == nullptr) { throw std::length_error("Null pointer passed to MyString constructor"); } // 其他初始化代码,例如分配内存和复制字符串 } };
在这个简化的实现中,我们检查传入的const char*
指针是否为空。如果为空,则抛出一个std::length_error
异常。在实际的std::string
实现中,可能会执行类似的检查。但请注意,不同的C++标准库实现可能会有不同的行为,这只是一个示例。
我的编译器版本是g++ (GCC) 7.3.1 20180303 (Red Hat 7.3.1-5),平台是centos,帮我找出检查并抛出异常的代码的具体位置
在 GCC 7.3.1 的 libstdc++ 实现中,std::string
的构造函数并没有明确地检查空指针并抛出 std::length_error
。但是,根据实际行为,当使用空指针初始化 std::string
时,可能会间接导致抛出异常。
这是因为当调用 std::string
的构造函数时,它会尝试计算传入的 const char*
的长度,这可能会导致未定义行为和异常。要找到这部分代码,请查看 basic_string.h
中的以下部分:
// file: /usr/include/c++/7.3.1/bits/basic_string.h // 构造函数模板 template<typename _InputIterator> basic_string(_InputIterator __beg, _InputIterator __end, const _Alloc& __a = _Alloc());
这个构造函数模板是处理从输入迭代器范围构造字符串的情况。const char*
会被视为一个输入迭代器,因此这个构造函数会被调用。在这个构造函数内部,__beg
和 __end
分别表示输入范围的开始和结束,但这些值在构造函数内部是未知的。由于传入了空指针,试图计算字符串的长度可能会导致内存访问错误。
为了避免使用空指针初始化 std::string
,您应该始终使用合适的构造函数,如创建空字符串或传递有效的 const char*
。
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc()); 这个函数的实现在哪个文件
对不起,我之前没有提供正确的构造函数实现。在您提到的 GCC 7.3.1 中,std::string
的构造函数,接受一个 const char*
参数的实现位于 basic_string.tcc
文件中。以下是源代码的一部分:
// file: /usr/include/c++/7.3.1/bits/basic_string.tcc template<typename _CharT, typename _Traits, typename _Alloc> basic_string<_CharT, _Traits, _Alloc>:: basic_string(const _CharT* __s, const _Alloc& __a) : _M_dataplus(_S_local_data(), __a) { _M_construct(__s, __s + traits_type::length(__s)); }
这里,_M_construct
函数被调用,接收两个参数:一个表示输入范围的开始,另一个表示输入范围的结束。在这种情况下,traits_type::length(__s)
会被调用来计算字符串的长度。这个调用可能会导致未定义行为,如果传入的 const char*
是一个空指针。
之前我搞错了,不是抛出length_error,是抛出logic_error
抱歉给您带来了困扰。根据您的描述,使用空指针初始化 std::string
时抛出的异常是 std::logic_error
。在 GCC 7.3.1 的 libstdc++ 实现中,std::string
的构造函数并没有明确地检查空指针并抛出 std::logic_error
。但是,根据实际行为,当使用空指针初始化 std::string
时,可能会间接导致抛出异常。
如前所述,问题可能出现在以下构造函数的实现中:
// file: /usr/include/c++/7.3.1/bits/basic_string.tcc template<typename _CharT, typename _Traits, typename _Alloc> basic_string<_CharT, _Traits, _Alloc>:: basic_string(const _CharT* __s, const _Alloc& __a) : _M_dataplus(_S_local_data(), __a) { _M_construct(__s, __s + traits_type::length(__s)); }
当传入的 const char*
是一个空指针时,试图计算字符串的长度可能会导致未定义行为。在您的情况下,这可能导致抛出 std::logic_error
异常。
为了避免这个问题,您应该始终使用合适的构造函数,如创建空字符串或传递有效的 const char*
。
template<typename _CharT, typename _Traits, typename _Alloc> template <typename _InIterator> _CharT* basic_string<_CharT, _Traits, _Alloc>:: _S_construct(_InIterator __beg, _InIterator __end, const _Alloc& __a, forward_iterator_tag) { #if _GLIBCXX_FULLY_DYNAMIC_STRING == 0 if (__beg == __end && __a == _Alloc()) return _S_empty_rep()._M_refdata(); #endif // NB: Not required, but considered best practice. if (__gnu_cxx::__is_null_pointer(__beg) && __beg != __end) __throw_logic_error(__N("basic_string::_S_construct null not valid")); const size_type __dnew = static_cast<size_type>(std::distance(__beg, __end)); // Check for out_of_range and length_error exceptions. _Rep* __r = _Rep::_S_create(__dnew, size_type(0), __a); __try { _S_copy_chars(__r->_M_refdata(), __beg, __end); } __catch(...) { __r->_M_destroy(__a); __throw_exception_again; } __r->_M_set_length_and_sharable(__dnew); return __r->_M_refdata(); }
我找到抛出logic_error的代码了,跟你说的不太一样,是我错了还是你错了
非常抱歉,我之前的回答没有找到正确的源代码。您找到的代码正是导致抛出 std::logic_error
异常的地方。在这段代码中,_S_construct
函数在接受输入迭代器范围作为参数时,检查了 __beg
是否为 nullptr
。如果检测到空指针并且范围非空(__beg != __end
),则抛出 std::logic_error
异常。
以下是这段代码的关键部分:
if (__gnu_cxx::__is_null_pointer(__beg) && __beg != __end) __throw_logic_error(__N("basic_string::_S_construct null not valid"));
这是在 GCC 7.3.1 的 libstdc++ 实现中进行空指针检查的地方。非常感谢您的耐心,并指出了我之前的回答错误。为了避免这个问题,您应该始终使用合适的构造函数,如创建空字符串或传递有效的 const char*
。
// TBD: DPG annotate template<typename _CharT, typename _Traits, typename _Alloc> basic_string<_CharT, _Traits, _Alloc>:: basic_string(const _CharT* __s, const _Alloc& __a) : _M_dataplus(_S_construct(__s, __s ? __s + traits_type::length(__s) : __s + npos, __a), __a) { }
这里的npos是哪里来的
在这个basic_string
构造函数实现中,npos
是std::string
(实际上是std::basic_string
)的一个静态常量成员,表示最大的可能字符串大小。它的类型是std::string::size_type
,通常是std::size_t
类型。npos
的值通常设置为static_cast<size_type>(-1)
,这意味着它是无符号整数类型的最大值。
在您提供的代码片段中,npos
被用作一个条件表达式的一部分。这个表达式决定了在调用_S_construct
函数时作为范围结束迭代器传递的值。当__s
不是空指针时,范围结束迭代器为__s + traits_type::length(__s)
,而当__s
是空指针时,范围结束迭代器为__s + npos
。
_S_construct(__s, __s ? __s + traits_type::length(__s) : __s + npos, __a)
这样,当传入一个空指针时,_S_construct
函数会收到一个非法的范围。由于我们在前面看到的_S_construct
函数中的空指针检查,这将导致抛出std::logic_error
异常。
请注意,这个实现的行为可能因编译器和标准库版本而异。在其他版本的实现中,可能会有不同的检查和异常。但在您指定的 GCC 7.3.1 版本中,这就是引发异常的地方。