检查 std::string.c_str() 的返回值
Inspect the returning value of std::string.c_str()
在 C++ Primer 5th Edition 中,它说:
The Array returned by c_str is not guaranteed to be valid indefinitely.
所以我做了一个测试:
// c_str exploration
std::string strTest = "This is a test";
const char* s1 = strTest.c_str();
strTest = "This is b test";
std::cout << s1 << std::endl;
因为s1是一个指针,所以它肯定显示了新的值。但是,当我将值更改为不同长度的字符串时,它通常会显示一些垃圾:
// c_str exploration
std::string strTest = "This is a test";
const char* s1 = strTest.c_str();
strTest = "This is b testsssssssssssssssssssssssssss";
std::cout << s1 << std::endl;
我想是因为返回的C String已经固定了结束空字符的位置,所以当长度改变时,一切都失效了。令我惊讶的是,有时即使我将字符串更改为新长度后它仍然有效:
// c_str exploration
std::string strTest = "This is a test";
const char* s1 = strTest.c_str();
strTest = "This is b tests"; // Note the extra s at the end
std::cout << s1 << std::endl;
第二题:
我也不确定为什么 std::cout << s1
打印内容而不是 C 字符串的地址。虽然以下代码按我的预期打印了 Integer 的地址:
int dim = 42;
int* pdim = &dim;
std::cout << pdim << std::endl;
这会按预期打印出字符 'T':
std::cout << *s1 << std::endl;
我的假设是 std::cout 会自动转换,但请多多指教。
第二个问题
std::ostream::operator<<
被重载以接受整数、const char*
和其他几种基本数据类型。它们中的每一个实际上都有一个稍微不同的功能,任何不是您打印的原始类型的东西都必须有一个定义的转换为原始类型。
从c_str()
返回的指针保证在string
被修改之前有效。当它被修改时(通过调用非常量成员函数),string
可能必须在内部分配一个新的内存缓冲区,这会使指针无效。没有具体说明何时以及如何发生这种情况。
对于第二个问题:operator <<
有不同的重载,string
的重载打印其内容。
第一个问题
如果不修改字符串,std::c_str()
返回的指针仍然有效。来自 cppreference.com:
The pointer obtained from c_str()
may be invalidated by:
- Passing a non-const reference to the string to any standard library function, or
- Calling non-const member functions on the string, excluding
operator[]
, at()
, front()
, back()
, begin()
, rbegin()
, end()
and rend()
.
在您发布的代码中,
std::string strTest = "This is a test";
const char* s1 = strTest.c_str();
strTest = "This is b tests"; // This line makes the pointer invalid.
然后使用指针访问字符串是未定义的行为。
std::cout << s1 << std::endl; // Undefined behavior.
在那之后,试图理解代码的作用是没有意义的。
第二个问题
标准库在 std::ostream
和 char const*
之间提供了运算符重载函数,因此可以以合理的方式打印 C 风格的字符串。当您使用:
std::cout << "Hello, World.";
您可能希望看到 Hello, World.
作为输出,而不是指向该字符串的指针的值。
出于超出此答案范围的原因,该函数重载作为非成员函数实现。
template< class CharT, class Traits >
basic_ostream<CharT,Traits>& operator<<( basic_ostream<CharT,Traits>& os,
const CharT* s );
替换所有与模板相关的标记后,该行将转换为:
std::ostream& operator<<(std::ostream& os, const char* s );
您可以在 cppreference.com.
查看非成员重载函数列表
第一题:
c_str 文档说明如下,比书上说的更清楚一点,因为它说明了何时可能失效:
The pointer returned may be invalidated by further calls to other
member functions that modify the object.
我做了一个快速测试:当你更新字符串时,s1 指向的地址会失效(即 strTest.c_str()
returns 一个不同的值)。
文档中并不清楚哪些成员函数使指针无效,但可以肯定地说,如果要使用 c_str
指针,则不应对原始字符串变量进行操作.
第二题:
cout
从空字符推断字符数组的结尾。当它是您测试过的整数指针时,这不起作用。
在 C++ Primer 5th Edition 中,它说:
The Array returned by c_str is not guaranteed to be valid indefinitely.
所以我做了一个测试:
// c_str exploration
std::string strTest = "This is a test";
const char* s1 = strTest.c_str();
strTest = "This is b test";
std::cout << s1 << std::endl;
因为s1是一个指针,所以它肯定显示了新的值。但是,当我将值更改为不同长度的字符串时,它通常会显示一些垃圾:
// c_str exploration
std::string strTest = "This is a test";
const char* s1 = strTest.c_str();
strTest = "This is b testsssssssssssssssssssssssssss";
std::cout << s1 << std::endl;
我想是因为返回的C String已经固定了结束空字符的位置,所以当长度改变时,一切都失效了。令我惊讶的是,有时即使我将字符串更改为新长度后它仍然有效:
// c_str exploration
std::string strTest = "This is a test";
const char* s1 = strTest.c_str();
strTest = "This is b tests"; // Note the extra s at the end
std::cout << s1 << std::endl;
第二题:
我也不确定为什么 std::cout << s1
打印内容而不是 C 字符串的地址。虽然以下代码按我的预期打印了 Integer 的地址:
int dim = 42;
int* pdim = &dim;
std::cout << pdim << std::endl;
这会按预期打印出字符 'T':
std::cout << *s1 << std::endl;
我的假设是 std::cout 会自动转换,但请多多指教。
第二个问题
std::ostream::operator<<
被重载以接受整数、const char*
和其他几种基本数据类型。它们中的每一个实际上都有一个稍微不同的功能,任何不是您打印的原始类型的东西都必须有一个定义的转换为原始类型。
从c_str()
返回的指针保证在string
被修改之前有效。当它被修改时(通过调用非常量成员函数),string
可能必须在内部分配一个新的内存缓冲区,这会使指针无效。没有具体说明何时以及如何发生这种情况。
对于第二个问题:operator <<
有不同的重载,string
的重载打印其内容。
第一个问题
如果不修改字符串,std::c_str()
返回的指针仍然有效。来自 cppreference.com:
The pointer obtained from
c_str()
may be invalidated by:
- Passing a non-const reference to the string to any standard library function, or
- Calling non-const member functions on the string, excluding
operator[]
,at()
,front()
,back()
,begin()
,rbegin()
,end()
andrend()
.
在您发布的代码中,
std::string strTest = "This is a test";
const char* s1 = strTest.c_str();
strTest = "This is b tests"; // This line makes the pointer invalid.
然后使用指针访问字符串是未定义的行为。
std::cout << s1 << std::endl; // Undefined behavior.
在那之后,试图理解代码的作用是没有意义的。
第二个问题
标准库在 std::ostream
和 char const*
之间提供了运算符重载函数,因此可以以合理的方式打印 C 风格的字符串。当您使用:
std::cout << "Hello, World.";
您可能希望看到 Hello, World.
作为输出,而不是指向该字符串的指针的值。
出于超出此答案范围的原因,该函数重载作为非成员函数实现。
template< class CharT, class Traits >
basic_ostream<CharT,Traits>& operator<<( basic_ostream<CharT,Traits>& os,
const CharT* s );
替换所有与模板相关的标记后,该行将转换为:
std::ostream& operator<<(std::ostream& os, const char* s );
您可以在 cppreference.com.
查看非成员重载函数列表第一题:
c_str 文档说明如下,比书上说的更清楚一点,因为它说明了何时可能失效:
The pointer returned may be invalidated by further calls to other member functions that modify the object.
我做了一个快速测试:当你更新字符串时,s1 指向的地址会失效(即 strTest.c_str()
returns 一个不同的值)。
文档中并不清楚哪些成员函数使指针无效,但可以肯定地说,如果要使用 c_str
指针,则不应对原始字符串变量进行操作.
第二题:
cout
从空字符推断字符数组的结尾。当它是您测试过的整数指针时,这不起作用。