0

这是“从 C++ 开始 - 从控制结构到对象,6e”第 586 页的代码修改版本:

#include <iostream>
using namespace std;

int countChars(char *, char);

int main()
{
    const int SIZE = 5;
    char userString[SIZE];
    char letter;

    cout << "Enter a string: ";
    cin.getline(userString, 10);

    letter = '\0';
    cout << "a appears ";
    cout << countChars(userString, 'a') << " times.\n";

    cin >> letter;
    return 0;
}

int countChars(char *strPtr, char ch)
{
    int times = 0;
    while (*strPtr != '\0')
    {
        if (*strPtr == ch)
            times++;
        strPtr++;
    }
    return times;
}

现在运行程序并输入“aaaabba”。

现在,我专门尝试在这里引入不正确的内存写入。例如,我声明 char 数组大小为 5,但在提示时输入超过 4 个(5 减去 \0 的长度)字符。

假设系统在“userString”之后为“letter”分配了内存,那么当我向“letter”写入内容时,它应该覆盖“扩展”用户字符串中的相应位置。

所以内存应该是这样的:[a][a][a][a][\0][b][a][\0]。

然后,当我运行 countChars 函数时,根据本书,它应该停在 '\0' 字符处,即前四个 a 之后。

按照这个逻辑,它应该输出字符串中有 4 个 a。

实际上,程序说有 5 个 a。

我的推理错误在哪里?

编辑#1:这不是书中的代码。这是修改后的代码。

编辑#2:我专门更改了代码以引入字符串溢出。我是故意这样做的,因为我想看看记忆是否真的像我想的那样工作。所以我想听到的是关于为什么这个错误不能像我期望的那样工作的可靠解释。

编辑#3:编译器确实抱怨堆栈损坏,但我按“继续”,因为我想看看会发生什么。

谢谢你。

4

4 回答 4

2

即使您只为 5 个字符分配了空间,也没有检查,因此您的程序会厚颜无耻地覆盖数组后面地址中的任何内容。在您的特定情况下,您(不)幸运并且没有看到崩溃 - 但实际上这是未定义的行为。唯一的 NUL 终止符位于您读取的字符串的末尾,而不是第五个位置,因此您会看到所有as。这不是正确的做事方式……

于 2011-11-06T02:03:19.370 回答
1

C 或 C++ 中没有规定局部变量以任何特定顺序分配。甚至根本就在堆栈上。你char可能只存在于一个 CPU 寄存器中。它可能出现在数组之前。数组大小可能会填充到最接近的 16 个字节,以使 SSE 操作更容易。

于 2011-11-06T02:12:07.867 回答
1

编译器绝对没有义务letteruserString. 如果你在调试模式下运行,它会在中间分配调试信息。如果您在发布模式下运行,它可能在一个寄存器中,并且堆栈上可能有任何东西。

于 2011-11-06T02:12:54.700 回答
1

如果您想知道堆栈变量是如何相对于彼此布置的,为什么不输入一个

cout << ((int)userString) << endl << ((int)&letter) << endl;

?

正如其他回答者指出的那样,不能保证任何特定的布局,但以上内容至少会告诉您编译器版本使用优化设置是如何布局的。

(警告:Zan Lynx 非常正确地提到,它只letter允许在 CPU 寄存器中,而不是在堆栈中。但是,上面的行包括&letter,这意味着编译器必须放入letter堆栈,因为寄存器没有内存地址。因此,通过阻止编译器优化,上述行实际上可能会影响程序的行为。您可能会突然发现只有四个 a!)

于 2011-11-06T02:27:46.963 回答