我有一段简单的代码可以反转一个字符串:
# 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
是否必须为零终止(也许它只是正常工作。因为偶然在内存位置0
恰好有一个ch + str.length()
因此,我写了以下快速测试:
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)
如何导致&#34; dbca&#34;在ch[str.length]
时,有&#39; a&#39;我要么预计会出现内存错误,要么就会出现内存错误或者#34; dbcaa&#34;结果。
答案 0 :(得分:1)
它被称为未定义的行为。可能是在ch的最后一个地址之后有一个零,所以它似乎可以工作。但是你要覆盖从内存管理器分配的内存,这会破坏它,所以你会在更大的应用程序中遇到麻烦。 内存管理器可以在调试版本中保留更多字节以用于调试目的。尝试发布版本,看看会发生什么
答案 1 :(得分:1)
在这一行之前你做了什么并不重要:
str = string(ch);
原因是上面的行可能会分配内存,而内存管理器可能已经使用直接在ch
缓冲区后面的内存作为分配空间。所以你之前在那里写的a
字符已经消失了。或者在构造str
期间发生了其他事情,假设您之前写入的空间可用。
如果您想确切知道,使用您的调试器。 std::string
构造函数和实现将告诉您究竟发生了什么(也就是说,如果您的程序在上面的代码行之前引入了未定义的行为,甚至可以实现这一点。)
答案 2 :(得分:0)
您的代码完全被破坏,具有未定义的行为。具体地说...
ch[j] = ch[i];
...从ch[i]
读取未经初始化的记忆 - 正如bgoldst评论的那样,它可能意味着str[i]
,然后 - 即使这并未使任何期望无效程序行为......
str = string(ch);
...使用ch
尝试构建,它指向仍然可以包含任何内容的未初始化的内存,并且将被扫描直到NUL碰巧被击中,某些访问冲突导致程序崩溃,或其他任何未定义的行为表现出来。如果您将循环修复为从str
复制,那么您可能希望这可以解决NUL终止的缺失问题:
str = string(ch, str.length());
也许这个含糊不清的问题是&#34;尽管出现上述错误,我几乎不可能观察到(声明的)dbca
输出?&#34 ; 。我要说:
dbca
不是dcba
- 您实际看到了什么?
内存中的垃圾字符可能无法在您的终端上执行任何操作,并且很可能尝试从分配了ch
的位置进行打印时不会显示任何内容,或者例如打印了一些废话,然后是一个明确的回到开头,删除 - 先前,退格等字符代码,然后恰好击中std::string
对象分配的内存(似乎缺少一个短的 - 字符串优化缓冲区),因此也显示其内容。
所以 - 在某种程度上定义行为的方式构成你的程序的证据并不是那么统计上令人惊讶......