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」に何かを書き込むと、「extended」userString の対応する場所が上書きされることになります。

したがって、メモリは [a][a][a][a][\0][b][a][\0] のようになります。

次に、countChars 関数を実行すると、本によると、最初の 4 つの a の直後にある「\0」文字で停止するはずです。

このロジックにより、文字列に 4 つの a があることが出力されるはずです。

実際には、プログラムは 5 つの a があると言っています。

私の推論のどこが間違っているのでしょうか?

編集 #1:これは本のコードではありません。それは変更されたコードです。

編集 #2:特に文字列オーバーフローを導入するためにコードを変更しました。意図的にこれを行ったのは、メモリが実際に自分の思うように機能するかどうかを確認したいからです。ですから、私が聞きたいのは、バグが期待どおりに機能しない理由についての確かな説明です。

編集 #3:コンパイラはスタックの破損について不平を言いますが、何が起こるかを見たいので、「続行」を押します。

ありがとうございました。

4

4 に答える 4

2

5 文字分のスペースしか割り当てていないにもかかわらず、チェックは行われず、その結果、プログラムは配列の後のアドレスにあるものを堂々と上書きします。あなたの特定のケースでは、(不) 幸運で、クラッシュは見られませんでしたが、実際にはこれは未定義の動作です。唯一の NUL ターミネータは、5 番目の位置ではなく、読み取った文字列の末尾にあるため、すべてのas が表示されます。これは物事を行う正しい方法ではありません...

于 2011-11-06T02:03:19.370 に答える
1

スタック変数が相互にどのように配置されているか疑問に思っている場合は、

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

?

他の回答者が指摘しているように、特定のレイアウトの保証はありませんが、上記は少なくとも、最適化設定を使用してコンパイラのバージョンによってどのようにレイアウトされているかを示します。

(注意: Zan Lynx は、letterCPU レジスターだけに存在することが許可され、スタック上に存在することはまったく許可されていないことを非常に正しく述べています。ただし、上記の行には が含まれています。&letterこれは、コンパイラーがletterスタックに配置する必要があることを意味します。メモリアドレスを持っていません. したがって, 上記の行は実際にあなたのプログラムの振る舞いに影響を与える可能性があります. コンパイラの最適化を妨げることによって.

于 2011-11-06T02:27:46.963 に答える
1

C または C++ には、ローカル変数が特定の順序で割り当てられるという規則はありません。または、まったくスタックにいることさえあります。CPUcharレジスタにのみ存在する可能性があります。配列の前に来るかもしれません。配列サイズは、SSE 操作を容易にするために、最も近い 16 バイトにパディングされる場合があります。

于 2011-11-06T02:12:07.867 に答える
1

letterコンパイラには、後に割り当てる義務はまったくありませんuserString。デバッグ モードで実行している場合は、途中でデバッグ情報が割り当てられます。リリース モードで実行している場合は、おそらくレジスタにあり、スタックには何かが存在する可能性があります。

于 2011-11-06T02:12:54.700 に答える