【问题标题】:C++ program compiled in windows produces different output在 Windows 中编译的 C++ 程序产生不同的输出
【发布时间】:2015-07-13 16:50:46
【问题描述】:

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

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

【问题讨论】:

  • 你读入s数组的字符串有多长?任何超过 4 个字符(加上 NUL 终止符)的内容都会溢出数组并丢弃以下内存,可能包括您的 counter 变量。

标签: c++ linux windows gcc


【解决方案1】:

这是缓冲区溢出。最有可能的是,当您在 Windows 上编译时,counter 变量紧跟在内存中的 s[5] 变量之后,如下所示:

+----+----+----+----+----+----+----+----+----+
| ?? | ?? | ?? | ?? | ?? | 01 | 00 | 00 | 00 |
+----+----+----+----+----+----+----+----+----+
 \________ s[5] ________/ \____ counter ____/

由于 Windows 是 little-endian,它存储为 01 00 00 00 而不是您可能期望的 00 00 00 01?? 只是表示我们还不知道那里有什么——它可能是任何东西。

现在,假设您输入“Hardy”并按 Enter。在 ASCII 中,它转换为字节序列 48 61 72 64 79 0D 0A(最后两个是行尾,在 UNIX 上,0D 将被省略)。这就是cin &gt;&gt; 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 系统是大端的,这意味着内存的布局是这样的:

+----+----+----+----+----+----+----+----+----+
| ?? | ?? | ?? | ?? | ?? | 00 | 00 | 00 | 01 |
+----+----+----+----+----+----+----+----+----+

因此,即使您读入 5 个字符,最终的空终止符也只会替换已经存在的 0。当然,如果是这个原因,那么读入 6 个字符就真的很麻烦了。

你如何解决它?问题是,要保存长度为n 的字符串,字符数组的长度必须为n+1。所以你可以这样做:

char s[6];

或者更好的是,使用字符串:

std::string s;

(为此您需要#include &lt;string&gt;。)

【讨论】:

  • 很好地使用了 ASCII 艺术。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-01
  • 2013-09-13
  • 2020-05-14
  • 2019-02-03
相关资源
最近更新 更多