コンピューターは、物の保管場所をどのように覚えていますか?


32

コンピューターが変数を保存するとき、プログラムが変数の値を取得する必要があるとき、コンピューターはどのようにしてその変数の値をメモリ内で探すべきかを知るのでしょうか?


17
そうではありません。「コンピューター」は完全に忘れられています。すべてのアドレスをハードコーディングする必要があります。(これは少し単純化されていますが、あまり簡単ではありません。)
ラファエル

1
@Raphael:それを「ベースアドレスをハードコーディングする必要がある」と一般化しましょう。
フレネル

変数を宣言するたびに、コードの実行を担当するプログラムには、ハッシュテーブル(別名名前空間)のアドレスに変数名が含まれます。。私はよく、このような細かいと知り合いになるために、コンピュータプログラム(SICP)の著書「構造と実装を読んでお勧めしたい
Abhirathマヒパール

ソースプログラムは変数を使用します。コンパイラーまたはインタープリターは、それを実装する方法を決定します。コンピューターが実行するための命令を生成し、それらの命令が以前の命令が格納した場所から値をフェッチすることを確認する必要があります。
PJTraill

1
@AbhirathMahipal:変数はコンパイル時または実行時にアドレスを持つ必要はありません。「名前空間」は言語の概念であり、テーブル(ハッシュまたはその他)は実装の詳細です。名前は、実行時にプログラム内で持続する必要があります。
PJTraill

回答:


31

コンパイラ構築の素晴らしい世界をご覧になることをお勧めします!答えは、少し複雑なプロセスだということです。

あなたに直観を与えようとするために、変数名はプログラマのためだけに存在することを覚えておいてください。コンピューターは最終的にすべてをアドレスに変換します。

ローカル変数は(一般に)スタックに保存されます。つまり、関数呼び出しを表すデータ構造の一部です。関数が(おそらく)使用する変数の完全なリストは、その関数を調べることで決定できるため、コンパイラーは、この関数に必要な変数の数と各変数が使用するスペースの量を確認できます。

スタックポインターと呼ばれるちょっとした魔法があります。これは、現在のスタックが始まるアドレスを常に保存するレジスタです。

各変数には、「スタックオフセット」が与えられます。これは、スタックのどこに格納されるかです。次に、プログラムが変数にアクセスする必要がある場合x、コンパイラはに置き換えxられSTACK_POINTER + x_offset、メモリに格納されている実際の物理的な場所を取得します。

これが、CまたはC ++ を使用するとき、mallocまたはnewCまたはC ++でポインタを戻す理由です。ヒープに割り当てられた値がメモリのどこにあるのかを正確に判断することはできません。そのため、ポインタを保持する必要があります。そのポインターはスタック上にありますが、ヒープを指します。

関数呼び出しと戻り値のスタックの更新の詳細は複雑なので、興味があればドラゴンブックまたはタイガーブックをお勧めします。


24

コンピューターが変数を保存するとき、プログラムが変数の値を取得する必要があるとき、コンピューターはどのようにしてその変数の値をメモリ内で探すべきかを知るのでしょうか?

プログラムはそれを伝えます。コンピューターには本来「変数」という概念がありません。これは完全に高レベルの言語です!

Cプログラムを次に示します。

int main(void)
{
    int a = 1;
    return a + 3;
}

そして、ここにコンパイルするアセンブリコードがあります:(で始まるコメント;

main:
    ; {
    pushq   %rbp
    movq    %rsp, %rbp

    ; int a = 1
    movl    $1, -4(%rbp)

    ; return a + 3
    movl    -4(%rbp), %eax
    addl    $3, %eax

    ; }
    popq    %rbp
    ret

「int a = 1;」の場合 CPUは「アドレスに値1を格納する(レジスタrbpの値から4を引いた値)」という命令を確認します。プログラムが値1を保存するので、値1を保存する場所を知っています。

同様に、次の命令は「アドレスの値(レジスタrbpの値から4を引いた値)をレジスタeaxにロードする」というものです。コンピューターは変数のようなことを知る必要はありません。


2
これをjmiteの答えに結びつけるの%rspが、CPUのスタックポインターです。%rbp現在の関数が使用するスタックのビットを参照するレジスタです。2つのレジスタを使用すると、デバッグが簡単になります。
–MSalters

2

コンパイラーまたはインタープリターは、変数の宣言を検出すると、その変数を格納するために使用するアドレスを決定し、そのアドレスをシンボルテーブルに記録します。その変数への後続の参照が検出されると、シンボルテーブルのアドレスが置き換えられます。

シンボルテーブルに記録されるアドレスは、レジスタ(スタックポインタなど)からのオフセットかもしれませんが、それは実装の詳細です。


0

正確な方法は、具体的に何を話しているか、どの程度深くしたいかによって異なります。たとえば、ファイルをハードドライブに保存することは、メモリに何かを保存することや、データベースに何かを保存することとは異なります。概念は似ていますが。また、プログラミングレベルでそれを行う方法は、コンピューターがI / Oレベルで行う方法とは異なる説明です。

ほとんどのシステムでは、何らかの種類のディレクトリ/インデックス/レジストリメカニズムを使用して、コンピューターがデータを検索してアクセスできるようにします。このインデックス/ディレクトリには、1つ以上のキーと、データが実際に配置されているアドレス(ハードドライブ、RAM、データベースなど)が含まれます。

コンピュータープログラムの例

コンピュータプログラムは、さまざまな方法でメモリにアクセスできます。通常、オペレーティングシステムはプログラムにアドレススペースを与え、プログラムはそのアドレススペースで必要な処理を実行できます。メモリ空間内の任意のアドレスに直接書き込むことができ、その方法を追跡できます。これは、プログラミング言語やオペレーティングシステムによって、またはプログラマの好みの技術によっても異なる場合があります。

他の回答のいくつかで述べたように、使用される正確なコーディングまたはプログラミングは異なりますが、通常は背後でスタックのようなものを使用します。現在のスタックが開始するメモリ位置を保存するレジスタがあり、そのスタックのどこに関数または変数があるかを知る方法があります。

多くの高レベルのプログラミング言語では、それはすべてあなたのために面倒を見ます。あなたがしなければならないのは、変数を宣言し、その変数に何かを保存することだけです。そうすれば、必要なスタックと配列がバックグラウンドで作成されます。

しかし、プログラマーはいつでも割り当てられたスペース内の任意のアドレスに直接書き込むことを選択できるので、多目的なプログラミングがいかに優れているかを考えると、実際には1つの答えはありません(それを可能にするプログラミング言語を使用していると仮定)。次に、その場所を配列に格納するか、プログラムにハードコードするだけで済みます(つまり、変数 "alpha"は常にスタックの先頭に格納されるか、割り当てられたメモリの最初の32ビットに格納されます)。

概要

そのため、基本的には、データの保存場所をコンピューターに伝えるメカニズムが背後で必要になります。最も一般的な方法の1つは、キーとメモリアドレスを含む何らかのインデックス/ディレクトリです。これはあらゆる種類の方法で実装され、通常はユーザーからカプセル化されます(プログラマからカプセル化されることもあります)。

参照:コンピューターは、物の保管場所をどのように覚えていますか?


0

テンプレートとフォーマットのおかげです。

プログラム/機能/コンピューターは実際には何がどこにあるかを知りません。何かが特定の場所にあることを期待しています。例を使用しましょう。

class simpleClass{
    public:
        int varA=58;
        int varB=73;
        simpleClass* nextObject=NULL;
};

新しいクラス「simpleClass」には、3つの重要な変数が含まれています。必要なときにデータを格納できる2つの整数と、別の「simpleClassオブジェクト」へのポインターです。簡単にするために、32ビットマシン上にいると仮定しましょう。「gcc」または別の「C」コンパイラは、データを割り当てるために使用するテンプレートを作成します。

単純型

まず、「int」などの単純なタイプのキーワードを使用すると、コンパイラによって実行可能ファイルの「.data」または「.bss」セクションにメモが作成されるため、オペレーティングシステムによって実行されると、データはプログラムで利用できます。'int'キーワードは4バイト(32ビット)を割り当て、 'long int'は8バイト(64ビット)を割り当てます。

セルごとに、変数がメモリにロードされるはずの命令の直後に変数が来る場合があるため、擬似アセンブリでは次のようになります。

...
clear register EAX
clear register EBX
load the immediate (next) value into EAX
5
copy the value in register EAX to register EBX
...

これは、EAXおよびEBXで値「5」で終了します。

プログラムの実行中、「5」以外のすべての命令が実行されます。即時ロードがそれを参照し、CPUがそれをスキップするためです。

この方法の欠点は、コードの中央に配列/バッファ/文字列を保持することは非現実的であるため、定数に対して実際にしか実用的ではないということです。したがって、一般的に、ほとんどの変数はプログラムヘッダーに保持されます。

これらの動的変数の1つにアクセスする必要がある場合、即値をポインターであるかのように扱うことができます。

...
clear register EAX
clear register EBX
load the immediate value into EAX
0x0AF2CE66 (Let's say this is the address of a cell containing '5')
load the value pointed to by EAX into EBX
...

これは、レジスタEAXの値「0x0AF2CE66」およびレジスタEBXの値「5」で終了します。レジスタに値を一緒に追加することもできるため、このメソッドを使用して配列または文字列の要素を見つけることができます。

もう1つの重要な点は、同様の方法でアドレスを使用するときに値を保存できるため、後でそれらのセルの値を参照できることです。

複合型

このクラスの2つのオブジェクトを作成する場合:

simpleClass newObjA;
simpleClass newObjB;

次に、最初のオブジェクトで使用可能なフィールドに2番目のオブジェクトへのポインターを割り当てることができます。

newObjA.nextObject=&newObjB;

これで、プログラムは最初のオブジェクトのポインタフィールド内で2番目のオブジェクトのアドレスを見つけることができるようになります。メモリでは、これは次のようになります。

newObjA:    58
            73
            &newObjB
            ...
newObjB:    58
            73
            NULL

ここで注意すべき非常に重要な事実は、「newObjA」と「newObjB」はコンパイル時に名前を持たないということです。これらは、何らかのデータがあると予想される場所にすぎません。したがって、&newObjAに2つのセルを追加すると、「nextObject」として機能するセルが見つかります。したがって、「newObjA」のアドレスと「nextObject」セルの相対位置がわかっている場合、「newObjB」のアドレスを知ることができます。

...
load the immediate value into EAX
&newObjA
add the immediate value to EAX
2
load the value in EAX into EBX

これは、「EAX」の「2 +&newObjA」および「EBX」の「&newObjB」で終わります。

テンプレート/フォーマット

コンパイラーがクラス定義をコンパイルするとき、フォーマットを作成する方法、フォーマットに書き込む方法、およびフォーマットから読み取る方法を実際にコンパイルしています。

上記の例は、2つの「int」変数を持つ単一リンクリストのテンプレートです。これらの種類の構成は、バイナリおよびnツリーと共に、動的メモリ割り当てにとって非常に重要です。n-aryツリーの実用的なアプリケーションは、ファイル、ディレクトリ、またはドライバ/オペレーティングシステムによって認識される他のインスタンスを指すディレクトリで構成されるファイルシステムです。

すべての要素にアクセスするために、構造体の上下に働きかけるインチワームについて考えてください。このように、プログラム/機能/コンピューターは何も知らず、単にデータを移動する命令を実行します。


ここで使用されている「テンプレート」と「フォーマット」という言葉は、私が見たコンパイラーやコンパイラーの教科書には現れておらず、同じ存在しないものに両方の単語を使用する理由はないようです。変数にはアドレスまたはオフセット、あるいはその両方がありますが、それだけで十分です。
user207421

数字、ファイル、配列、変数が抽象化されているように、データ配置の抽象化であるため、この言葉を使用しています。
ミントフレッシュ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.