我正在解决一个涉及递增计数器并显示它的问题。我初始化和增加变量的方式似乎很正常。请参阅计数器变量
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char s[5];
int counter = 1;
while (cin >> s && (strcmp(s, "*") != 0))
{
cout << "Case " << counter++ << ": Hajj-e-A";
if (s[0] == 'H')
{
cout << "kbar\n";
}
else if (s[0] == 'U')
{
cout << "sghar\n";
}
}
}
但程序神秘地显示了错误的结果。它没有正确地增加从1开始的值。见输出。
Case 1: Hajj-e-Akbar
Case 0: Hajj-e-Asghar
Case 1: Hajj-e-Akbar
Case 0: Hajj-e-Asghar
但是当我尝试通过使用Linux的http://www.tutorialspoint.com/compile_cpp_online.php进行编译和运行时,它产生了正确的结果。该计划也被在线法官接受。
Case 1: Hajj-e-Akbar
Case 2: Hajj-e-Asghar
Case 3: Hajj-e-Akbar
Case 4: Hajj-e-Asghar
任何人都可以指出这背后的谜团吗?为什么Windows编译的代码会产生奇怪的结果?非常感谢!
答案 0 :(得分:8)
这是一个缓冲区溢出。最有可能的是,当您在Windows上编译时,counter
变量紧跟在内存中的s[5]
变量之后,如下所示:
+----+----+----+----+----+----+----+----+----+
| ?? | ?? | ?? | ?? | ?? | 01 | 00 | 00 | 00 |
+----+----+----+----+----+----+----+----+----+
\________ s[5] ________/ \____ counter ____/
由于Windows是little-endian,因此将其存储为01 00 00 00
而不是00 00 00 01
,就像您期望的那样。 ??
只表示我们还不知道那里有什么 - 它可能是任何东西。
现在,让我们说你进入&#34; Hardy&#34;然后按Enter键。在ASCII中,转换为字节序列48 61 72 64 79 0D 0A
(最后两行是结束行,在UNIX上将省略0D
)。这就是cin >> s
对记忆的作用:
1. Read in 'H':
+----+----+----+----+----+----+----+----+----+
| 48 | ?? | ?? | ?? | ?? | 01 | 00 | 00 | 00 |
+----+----+----+----+----+----+----+----+----+
2. Read in 'a':
+----+----+----+----+----+----+----+----+----+
| 48 | 61 | ?? | ?? | ?? | 01 | 00 | 00 | 00 |
+----+----+----+----+----+----+----+----+----+
3. Read in 'r':
+----+----+----+----+----+----+----+----+----+
| 48 | 61 | 72 | ?? | ?? | 01 | 00 | 00 | 00 |
+----+----+----+----+----+----+----+----+----+
4. Read in 'd':
+----+----+----+----+----+----+----+----+----+
| 48 | 61 | 72 | 64 | ?? | 01 | 00 | 00 | 00 |
+----+----+----+----+----+----+----+----+----+
5. Read in 'y':
+----+----+----+----+----+----+----+----+----+
| 48 | 61 | 72 | 64 | 79 | 01 | 00 | 00 | 00 |
+----+----+----+----+----+----+----+----+----+
6. Read in '\r\n' (or on UNIX, just '\n'), but this isn't put into the memory.
Instead, cin realizes that it has finished reading, and closes off the string with a '\0':
+----+----+----+----+----+----+----+----+----+
| 48 | 61 | 72 | 64 | 79 | 00 | 00 | 00 | 00 |
+----+----+----+----+----+----+----+----+----+
\________ s[5] ________/ \____ counter ____/
糟糕!它覆盖了柜台!
为什么它在Linux上正常运行? Linux没有将两个变量放在内存中,或Linux系统是big-endian,这意味着内存的布局如下:
+----+----+----+----+----+----+----+----+----+
| ?? | ?? | ?? | ?? | ?? | 00 | 00 | 00 | 01 |
+----+----+----+----+----+----+----+----+----+
所以即使你读了5个字符,最后的空终止符只是替换已经存在的0。当然,如果这是原因,那么读6个字符就会搞砸了。
你是如何解决的?问题是,要保存长度为n
的字符串,字符数组的长度必须为n+1
。所以你可以这样做:
char s[6];
甚至更好,使用字符串:
std::string s;
(为此你需要#include <string>
。)