std::string assign,clear和operator[]的奇怪行为

strange behavior of std::string assign,clear and operator[]

本文关键字:operator assign string clear std      更新时间:2023-10-16

我观察到字符串操作的一些奇怪行为。

,

int main()
{
  std::string name("ABCDEFGHIJ");
  std::cout << "Hello, " << name << "!n";
  name.clear();
  std::cout << "Hello, " << name << "!n";
  name.assign("ABCDEF",6);
  std::cout << "Hello, " << name << "!n";
  std::cout << "Hello, " << name[8] << "!n";
}
输出:

Hello, ABCDEFGHIJ!
Hello, !
Hello, ABCDEF!
Hello, I!

string::clear实际上没有清除,因为即使在清除之后我也能够访问数据。根据文档,当我们访问超出边界的东西时,结果是未定义的。但这里我每次都得到相同的结果。谁能解释一下当我们调用clear或operator [].

时,它是如何在内存层工作的?

欢迎来到c++的神奇魅力——"未定义行为"。

name包含6个字符的字符串"ABCDEF"时,name[8]尝试访问字符串中不存在的成员,这是未定义行为。

这意味着该操作的结果完全没有意义。

c++标准没有定义访问字符串中不存在的成员字符的结果;因此出现了未定义的行为。该操作的潜在结果可能是:

  1. 字符串中给定位置的先前值

  2. 一些垃圾,随机字符

  3. 程序崩溃

  4. 每次执行程序的结果都不一样,从选项1到4中选择。

name.assign("六边形ABCDEF",6);

现在字符串的长度为6。因此,只能合法地访问0到5号元素。

std:: cout & lt; & lt;"你好"<<名称[8]& lt; & lt;"! n";

因此这是未定义行为。编译器可以自由地做任何它想做的事。不仅仅是语句,还包括整个程序,甚至包括前面的行!

此时,它返回先前位于该位置的字符。它可以返回其他任何东西,它可以崩溃,它可以完全跳过那个语句,它可以跳过赋值和许多其他有趣的事情(包括使守护进程从你的鼻子里飞出来!)。

我这么说是因为所有这些行为(除了守护进程)实际上都可以在各种情况下在野外观察到。

正如其他人所说,访问std::string之外的逻辑边界(即[0,size()],注意包括size())是未定义的行为,因此编译器可以使任何事情发生。

现在,你所看到的UB的特殊味道并没有什么特别意外的。

clear()只是将字符串的逻辑长度归零,但是它所使用的内存被保留(这实际上是标准所要求的,如果没有这种行为,相当多的代码将工作得更慢)。

考虑到没有理由浪费时间将旧数据归零,通过越界访问字符串,您可以看到该索引之前的内容。

如果你在clear()之后调用shrink_to_fit()方法,这可能会改变,它要求字符串释放它保留的所有额外内存。

我想添加到其他答案中,您可以使用std::string::at而不是使用operator[]

std::string::at进行边界检查,当您试图访问超出范围的元素时,将抛出std::out_of_range

我通过调试器运行了您的代码。注意字符串的容量。仍然是15。"assign"没有改变容量。所以你不会得到每个人都说的"垃圾"价值。你得到的是完全相同的数据存储在相同的位置。如前所述,字符串只是一个指向内存地址的指针。它将通过x字节来访问元素。Name[8]是一个常量值,它将到完全相同的内存位置。这是调试器

中字符串的图片