将 char 数组转换为字符串时为零终止
Zero termination when casting a char array to string?
我有一段简单的代码来反转一个字符串:
# 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
对象分配的内存(似乎缺少短字符串优化缓冲区(, 因此也显示了其内容。
所以 - 构成你的程序以某种方式定义行为的证据在统计上并不那么惊人......
- 为什么这个 c++ 代码打印出长度 5,当我打印出字符串时,程序会自动终止?
- 在字符串函数中抛出'char const*'实例后调用的终止
- 使用终止程序的指针在数组中输入字符串
- 如何告诉字符串流忽略null终止字符
- 当 NUL 字符被定义为字符串的一部分时,为什么 strlen() 不计算终止 NUL 字符的字节?
- 如何将非空终止字符串输出到 iostream,但保持格式
- 是否有一种安全的方法可以断言字符串视图是否以 null 终止?
- 自动截断和 null 终止缓冲区溢出中的字符串缓冲区
- 用户定义的字符串文字为字符串null终止
- 如何制作包含原始字符串终止符C++原始字符串
- 为什么具有单独字符的字符数组不像字符串文字那样以 null 终止符结尾?
- 终止字符串的 '