将 char 数组转换为字符串时为零终止

Zero termination when casting a char array to string?

本文关键字:终止 字符串 数组 转换 char      更新时间:2023-10-16

我有一段简单的代码来反转一个字符串:

# include <string>
string str = "abcd";
char *ch = new char[str.length()];
for (int i = 0, j = str.length() - 1; i < str.length(); i++, j--)
    ch[j] = str[i];
str = string(ch);
cout << str;

这工作正常,但是我想知道 char 数组*ch是否必须以零终止(也许它工作正常,因为碰巧在内存位置 ch + str.length() 处有一个0。因此,我编写了以下快速测试:

string str = "abcd";
char *ch = new char[str.length()];
for (int i = 0, j = str.length() - 1; i < str.length(); i++, j--)
    ch[j] = str[i];
// note: illegal memory access, just a quick test
ch[str.length()] = 'a';
str = string(ch);
cout << str;

在上面的代码中,确保*ch永远不会以零终止。令我惊讶的是,代码仍然可以正常工作,我无法解决这个问题。当str = string(ch) ch[str.length]有"a"时,如何导致"dbca";我要么期望内存错误,要么结果是"dbcaa"。

你在这一行之前做了什么并不重要:

str = string(ch);

原因是上面的行可能会分配内存,并且内存管理器可能已将内存直接用作ch缓冲区之后的内存作为分配的空间。 所以你之前在那里写的a角色已经消失了。 或者在构建str的过程中发生了其他事情,假设您之前写入的空间可用。

如果要确定,请使用调试器std::string构造函数和实现将告诉您究竟发生了什么(也就是说,如果您的程序甚至在上面的代码行之前引入了未定义的行为以来甚至走到了这一步(。

这被称为未定义的行为。可能是 ch 的最后一个地址后面有一个零,所以它看起来可以工作。但是您正在覆盖从内存管理器分配的内存,这会损坏它,因此您将在更大的应用程序中遇到麻烦。内存管理器可以在调试版本中保留更多字节用于调试目的。尝试发布版本,看看会发生什么

你的代码完全被破坏了,有未定义的行为。 具体说来。。。

ch[j] = ch[i];

。从未初始化的内存中读取ch[i] - 正如 bgoldst 评论的那样,它可能意味着str[i],然后 - 即使这并没有使程序行为的任何期望无效......

str = string(ch);

。尝试使用 ch 进行构造,该内存指向可能具有任何内容的仍未初始化的内存,并且将被扫描,直到碰巧命中 NULL、某些访问冲突导致程序崩溃或任何其他未定义的行为。 如果您修复了从 str 复制的循环,那么您可能希望这样做来应对缺少 NUL 终止的问题:

str = string(ch, str.length());

也许一个模糊有价值的问题是"尽管存在上述错误,我几乎不可能观察到(声称的(dbca输出吗? 对此,我会说:

  • dbca不是dcba——你实际看到了哪个?

  • 内存中的垃圾字符可能不会在您的终端上执行任何操作,并且很可能尝试从分配ch的地方打印任何可见的内容,或者例如打印一些废话,然后清除回到行首,删除上一个,退格等字符代码,然后碰巧击中std::string对象分配的内存(似乎缺少短字符串优化缓冲区(, 因此也显示了其内容。

所以 - 构成你的程序以某种方式定义行为的证据在统计上并不那么惊人......