0

関数では、パラメーターが最初に配置された後に定義されるため、ローカル変数がスタックに配置されることを読みました。

こちらにも記載されています

5 .すべての関数引数がスタックに置かれます。6. 関数内の命令が実行を開始します。7.ローカル変数は、定義されるとスタックにプッシュされます。

したがって、C++ コードが次のようになっている場合は、次のようになると思います。

#include "stdafx.h"
#include <iostream>

int main()
{

    int a = 555;
    int b = 666;
    int *p = &a;

    std::cout << *(p+1);
    return 0;
}

int 555 ここで integer に 4 バイトがあり、 xの最初の 8 ビットを含むスタック上のメモリ空間を呼び出すと、アドレスx + 4*(p+1)のメモリを調べる必要があります。

ただし、これの出力は、-858993460値に関係なく、常にそのようなものですint b。明らかにそのいくつかの標準値。もちろん、これは変数 b であるため、アクセスしてはならないメモリにアクセスしています。それはただの実験でした。

期待値が得られず、不正アクセスエラーになるのはなぜですか?

私の仮定はどこが間違っていますか?
何を-858993460表すことができますか?

4

2 に答える 2

2

他の人が言っていること (つまり、「そんなことはしないでください」) は絶対に真実です。そうしないでください。ただし、実際にあなたの質問に答えるにp+1は、呼び出し元のスタック フレームへのポインターまたはリターン アドレス自体を指している可能性が最も高いです。システムが保持するスタック ポインターは、何かをプッシュすると減少します。これは公式に言えば実装に依存しますが、私がこれまでに見たすべてのスタック ポインター (これは 16 ビット時代以降のものです) はこのようなものでした。したがって、あなたが言うように、ローカル変数が初期化されるときにスタックにプッシュされる場合は&a== &b + 1.

おそらくイラストは順番です。最適化なしで 32 ビット x86 用にコードをコンパイルし、esp関数を呼び出す前のスタック ポインターが 20 であるとします (記録としてはありそうにありません)。呼び出す行の直前のメモリは次のようになりますcout

4: 12 (value of p)
8: 666 (value of b)
12: 555 (value of a)
16: -858993460 (return address)

p+1はでpあるためint*、 は 16 です。この場所のメモリは、呼び出し元の関数に戻る必要があるため、読み取り保護されていません。

この回答は学術的なものであることに注意してください。コンパイラの最適化またはプロセッサ間の違いが予期しない結果を引き起こした可能性があります。ただし、スタックは通常下向きに成長するため、これまでに見た呼び出し規則を使用するプロセッサ アーキテクチャでは期待できませんp+1== &b

于 2013-10-06T02:45:32.683 に答える
1

あなたの仮定は理論的には真実です(CSの観点から)。

実際には、これらの結果を期待してそのようにポインター演算を行う保証はありません

たとえば、「すべての関数引数がスタックに配置される」という仮定は正しくありません。関数引数の割り当ては実装定義です (アーキテクチャによっては、レジスタまたはスタックを使用できます)。また、コンパイラは自由に使用できます。必要に応じてローカル変数をレジスタに割り当てます。

また、intサイズは 4 バイトであるため、ポインタに 4 を加算するとb」という仮定も誤りです。コンパイラは、 と の間aにパディングを追加bして、メモリ アラインメントを確保することができます。

ここでの結論は次のとおりです。低レベルのトリックを使用しないでください。それらは実装定義です。(私たちのアドバイスに関係なく)それをしなければならない場合でも、コンパイラがどのように機能し、どのようにコードを生成するかを知る必要があります。

于 2013-10-05T22:36:51.690 に答える