6

C プログラミングのテキストブックを調べていると、このコードが表示されます。

#include <stdio.h>

int j, k;
int *ptr;

int main(void)
{
    j = 1;
    k = 2;
    ptr = &k;
    printf("\n");
    printf("j has the value %d and is stored at %p\n", j, (void *)&j);
    printf("k has the value %d and is stored at %p\n", k, (void *)&k);
    printf("ptr has the value %p and is stored at %p\n", (void *)ptr, (void *)&ptr);
    printf("The value of the integer pointed to by ptr is %d\n", *ptr);

    return 0;
}

私はそれを実行し、出力は次のとおりです。

j の値は 1 で、0x4030e0 に格納されます

k の値は 2 で、0x403100 に格納されます

ptr の値は 0x403100 で、0x4030f0 に格納されます

ptr が指す整数の値は 2 です。

私の質問は、コンパイラを介してこれを実行しなかった場合、このコードを見るだけでこれらの変数へのアドレスをどのように知ることができるでしょうか? 変数の実際のアドレスを取得する方法がわかりません。ありがとう!

4

7 に答える 7

5

標準 C で変数の「正確なアドレスを知る」方法は、「%p」で出力する以外にありません。実際のアドレスは、コードを書くプログラマーの制御下にない多くの要因によって決定されます。それは、OS、リンカ、コンパイラ、使用されるオプション、およびおそらくその他の問題です。

とはいえ、組み込みシステムの世界では、この変数がこのアドレスに存在する必要があることを表現する方法があります。たとえば、外部デバイスのレジスタが実行中のプログラムのアドレス空間にマップされている場合などです。これは通常、リンカー ファイルまたはマップ ファイルと呼ばれるもので発生するか、整数値をポインターに代入する (キャストを使用) ことによって発生します。これらの方法はすべて非標準です。

ただし、日常的なさまざまなプログラムの目的のために、Cプログラムを作成する際のポイントは、変数がどこに格納されているかを気にする必要はなく、気にする必要がないということです。

于 2012-11-07T04:03:00.307 に答える
5

これが私の理解です:

C のメモリ内の絶対アドレスは指定されていません。言語に標準化されていません。このため、コードを見ただけではメモリ内の場所を知ることはできません。(ただし、同じコンパイラ、コード、コンパイラ オプション、ランタイム、およびオペレーティング システムを使用する場合、アドレスは一貫している可能性があります。)

アプリケーションを開発している場合、これは依存すべき動作ではありません。ただし、コンテキストによっては、2 つの物の位置の違いに依存する場合があります。たとえば、2 つの配列要素へのポインターのアドレスの違いを判断して、それらが離れている要素の数を判断できます。

ところで、変数のメモリ位置を使用して特定の問題を解決することを検討している場合は、この動作に依存せずにその方法を尋ねる別の質問を投稿すると役立つ場合があります。

于 2012-11-07T03:55:18.083 に答える
3

コンパイラは、「3 番目の整数グローバル変数」や「スタック ポインタから 36 バイト下に割り当てられた 4 バイト」などしか認識していません。これは、グローバル変数、サブルーチン (関数) へのポインター、サブルーチン引数、およびローカル変数を相対的な用語でのみ参照します。(C++ のポリモーフィック オブジェクトの余分なものは気にしないでください!) これらの相対参照は、オブジェクト ファイル (.o または .obj) に特別なコードとオフセット値として保存されます。

リンカーは、いくつかの詳細を入力できます。複数のオブジェクト ファイルを結合すると、これらの大ざっぱな場所の参照の一部が変更される場合があります。複数のコンパイル単位からのグローバルがマージされると、グローバル変数の場所はスペース (データ セクション) を共有します。リンカは、すべての順序を決定しますが、グローバル変数のセット全体の開始点を基準として記述します。その結果、最終的なオペコードを含む実行可能ファイルが作成されますが、アドレスはまだ大ざっぱで、相対オフセットに基づいています。

ローダーがすべての相対アドレスを実際のアドレスに置き換えるのは、実行可能ファイルがロードされるまではありません。これが可能になったのは、ローダー (またはそれが依存するオペレーティング システムの一部) がプロセスの仮想アドレス空間全体のどこにプログラムのオペコード (テキスト セクション)、グローバル変数 (BSS、データ セクション) を格納するかを決定するためです。コールスタックなど。ローダーは計算を行い、実行可能ファイルのすべての場所に実際のアドレスを書き込むことができます。通常は、「即時ロード」オペコードおよびメモリ アクセスを含むすべてのオペコードの一部として実行されます。

詳細については、Google の「再配置テーブル」を参照してください。特定のプラットフォームに関する詳細な説明については、http://www.iecc.com/linker/linker07.html (やや古い) を参照してください。

実際には、仮想メモリシステムによって仮想アドレスが物理アドレスにマップされ、セグメントまたはその他のメカニズムを使用して各プロセスを個別のアドレス空間に保持するという事実により、すべてが複雑になります。

于 2012-11-07T05:02:29.547 に答える
3

できません。

コンパイラが異なれば、変数を別の場所に置くことができます。一部のマシンでは、アドレスはとにかく単純な整数ではありません。

于 2012-11-07T03:56:24.400 に答える
1

Visual Studio などの一部のコンパイラには、アドレス空間レイアウトのランダム化 (ASLR) と呼ばれる機能があり、ウイルス対策機能としてプログラムをランダムなメモリ アドレスから開始することを指摘することで、既に提供されている回答をさらに発展させたいと思います。 . 出力にあるアドレスを考えると、それなしでコンパイルしたと思います(アドレスなしのプログラムはアドレス0x400000から始まると思います)。この情報源は、この質問への回答です。

つまり、ローカル変数が格納されるメモリアドレスを決定するのはコンパイラです。アドレスはコンパイラごとに変わる可能性が高く、ソース コードのバージョンごとに変わる可能性もあります。

于 2012-11-07T04:37:16.813 に答える
0

上記の回答を続けると、プロセスが独自の仮想アドレス空間で実行されることを忘れないでください (プロセスの分離)。これにより、プログラムが一部のメモリを破損しても、他の実行中のプロセスは影響を受けません。

プロセス分離: http://en.wikipedia.org/wiki/Process_isolation

プロセス間通信 http://en.wikipedia.org/wiki/Inter-process_communication

于 2012-11-07T04:26:56.047 に答える