在Windows中编译的C ++程序产生不同的输出

时间:2015-07-13 16:50:46

标签: c++ linux windows gcc

我正在解决一个涉及递增计数器并显示它的问题。我初始化和增加变量的方式似乎很正常。请参阅计数器变量

#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编译的代码会产生奇怪的结果?非常感谢!

1 个答案:

答案 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>。)