检查 std::string.c_str() 的返回值

Inspect the returning value of std::string.c_str()

本文关键字:str 返回值 std string 检查      更新时间:2023-10-16

在C++入门第5版中,它说:

c_str返回的数组不保证无限期有效。

所以我做了一个测试:

//  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 字符串已经固定了结尾空字符的位置,因此当长度更改时,它会使所有内容无效。令我惊讶的是,有时即使我将字符串更改为新长度,它仍然有效:

//  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 字符串的地址。虽然下面的代码按我的预期打印整数的地址:

int dim = 42;
int* pdim = &dim;
std::cout << pdim << std::endl;

这将打印出字符"T",正如预期的那样:

std::cout << *s1 << std::endl;

我的假设是 std::cout 会进行自动转换,但请给我更多关于这一点的讲座。

第一个问题

如果不修改字符串,std::c_str()返回的指针仍然有效。从 cppreference.com:

c_str()获得的指针可能因以下原因而失效:

  • 将对字符串的非常量引用传递给任何标准库函数,或
  • 在字符串上调用非常量成员函数,不包括operator[]at()front()back()begin()rbegin()end()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::ostreamchar 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()返回的指针在修改string之前有效。修改它时(通过调用非 const 成员函数(,string可能必须在内部分配新的内存缓冲区,从而使指针无效。没有具体说明何时以及如何发生这种情况。

对于第二个问题:operator <<有不同的重载,string打印其内容。

第一个问题:

c_str文档指出以下内容,这比书中所说的更清楚一些,因为它说明了何时可能无效:

返回的指针可能会因进一步调用其他指针而失效 修改对象的成员函数。

我做了一个快速测试:当您更新地址 s1 指向的字符串时,该字符串将失效(即strTest.c_str()返回不同的值(。

从文档中不太清楚哪些成员函数使指针无效,但可以肯定地说,如果要使用c_str指针,则不应对原始字符串变量进行操作。

第二个问题:

cout从空字符推断字符数组的结尾。当它是您测试过的整数指针时,这不起作用。

第二个问题

std::ostream::operator<<被重载以接收整数、const char*和其他几种基本数据类型。实际上,它们中的每一个都有一个略有不同的功能,并且您打印的任何非基元类型的内容都必须具有定义的转换为原始类型的原始类型。