web-dev-qa-db-ja.com

変数とメモリ位置の違いは何ですか?

最近私はポインターをフラッシュカードのように視覚的に説明しようとしています。

質問001:これは、コンピューターのメモリ内の場所の図です。そのアドレスが0x23452であることは本当ですか?どうして?

enter image description here

回答:はい、0x23452はコンピュータがこの場所を見つけることができる場所を示しているためです。


質問002:文字bがメモリロケーション0x23452に格納されているのは本当ですか?どうして?

enter image description here

Answer:いいえ、文字aは実際に格納されているためです。


質問003:ポインタがメモリロケーション0x23452内に格納されていることは本当ですか?どうして?

enter image description here

Answer:はい、メモリロケーション0x34501のアドレスがその中に格納されているためです。


質問004:ポインタがメモリロケーション0x23452内に格納されていることは本当ですか?どうして?

enter image description here

Answer:はい、別のメモリロケーションのアドレスがその中に格納されているためです。


今、私を心配させている部分について。ソフトウェアエンジニアが私へのポインタを次のように説明しました。

ポインタは、値が別の変数のメモリアドレスである変数です。

これまでに紹介した4つのフラッシュカードに基づいて、ポインタを少し異なる方法で定義します。

ポインタは、値が別のメモリロケーションのメモリアドレスであるメモリロケーションです。

変数はメモリ位置と同じであると言っても安全ですか?

そうでない場合、誰が正しいのですか?変数とメモリ位置の違いは何ですか?

38
progner

変数は、アルゴリズムの意図に沿った論理的な構造ですが、メモリの場所は、コンピュータの動作を説明する物理的な構造です。一般的に言えば、プログラムを実行するために、変数の論理的な概念とコンピューターのストレージの間に(コンパイラーによって生成された)マッピングがあります。

(アセンブリ言語でも、アルゴリズムとインテントに行く(論理)変数、および(物理)メモリ位置の概念がありますが、アセンブリではより複雑です。)

変数は高レベルの概念です。変数は、未知数(数学、プログラミングの割り当てなど)または値で置換できるプレースホルダー(プログラミングのように:パラメーター)を表します。

メモリの場所は低レベルの概念です。値を格納するために、場合によっては変数の値を格納するために、メモリロケーションを使用できます。ただし、CPUレジスタは、一部の変数の値を格納する別の方法です。 CPUレジスタも低(下位)レベルの格納場所ですが、名前だけでアドレスを持たないため、メモリの場所ではありません。

ある意味では、変数はプログラムの意図を表すための抽象化のメカニズムであり、メモリの場所は、ストレージと検索を提供する処理環境の物理エンティティです。

質問003:ポインタがメモリロケーション0x23452内に格納されていることは本当ですか?どうして?

はっきりとは言えない。アドレスとして機能する値が存在するからといって、それがそのアドレスであることを意味するのではなく、整数(10進数)‭144466である可能性があります。数値の表示方法だけに基づいて値の解釈を推測することはできません。

質問004:ポインタがメモリロケーション0x23452内に格納されていることは本当ですか?どうして?

これは確かに奇妙な質問です。彼らはボックスに基づいていくつかの仮定を期待していますが、アドレスはボックスごとに1ずつ増加することに注意してください。現代のどのコンピュータでも、これは各ボックスが1バイトを保持できることを意味します。バイトアドレス指定機能は、何十年もの間標準となっています。ただし、バイトは8ビットにすぎず、0〜255の範囲になります(符号なしの値の場合)。それでも、これらのアドレスのいずれかに格納されているはるかに大きな値を示しているため、非常に疑わしいものです。 (これは、Wordでアドレス指定されたマシンであれば動作する可能性がありますが、それはそうではありません。また、一部の教育用マシンはそうですが、今日のマシンはほとんどありません。)

これまでに紹介した4つのフラッシュカードに基づいて、ポインタを少し異なる方法で定義します。

ポインタは、値が別のメモリロケーションのメモリアドレスであるメモリロケーションです。

この考え方が正しい場合もありますが、ここでは比喩を混ぜています。変数の概念は、アルゴリズムとその意図に関係しています。すべての変数がメモリ位置を持っていると想定する必要はありません。一部の変数(特に配列)は、メモリロケーションがアドレス指定をサポートしているため、メモリロケーションを持っています(CPUレジスタにはインデックスを付けずに名前を付けることができるだけ).

実行のために、変数とステートメントとプロセッサのメモリ位置とプロセッサの命令シーケンスの間に論理的なマッピングがあります。値を変更できない変数(定数など)でも、メモリの場所は必ずしも必要ではありません。これは、値を自由に再現できるためです(たとえば、コンパイラによって生成されるコードシーケンスの必要に応じて)。

69
Erik Eidt

変数はメモリ位置と同じであると言っても安全ですか?

いいえ。変数とメモリの場所は、2つの異なる抽象化レベルの2つの抽象化です。変数とポインタはコード/言語レベルでの上位レベルの概念であり、メモリ位置はマシンレベルでの下位レベルの概念です。コードが実行可能ファイルにコンパイルされると、変数はなくなります。この方法でメモリの場所と変数について話そうとすると、カテゴリエラーになります。

変数はメモリを使用して実装できますが、必ずしもコンパイラが計算を最適化し、変数に関連するすべての計算をレジスタで完全に実行できる、または単一の変数を複数のメモリ位置に配置できる、または単一のメモリを使用できるため、常にではありません複数の変数の場所。

ポインタは、値が別のメモリロケーションのメモリアドレスであるメモリロケーションです。

この一連のフラッシュカードは非常に混乱しており、正しくないだけでなく、間違いもありません。

20
Lie Ryan

変数は言語構造です。それらには名前があり、スコープ内に存在し、コードの他の部分から参照される場合があります。これらはlogicalエンティティです。コンパイラは、観察可能な動作が言語標準で規定されている動作である限り、自由にこの言語構​​造を自由に実装できます。そのため、コンパイラーは、変数が不要であることを証明できれば、変数をどこに格納する必要もありません。

メモリの場所はハードウェアの概念です。それらは、仮想/物理メモリ内の場所を示します。すべてのメモリロケーションには、1つの物理アドレスと、それを操作するために使用できる任意の量の仮想アドレスがあります。ただし、各メモリ位置には常に正確に1バイトが格納されます。

ポインタは特別な種類のvalues何かがポインタであると言うことは、何かがdouble。これは、値に使用されるビット数とそれらのビットの解釈方法を示しますが、この値が変数に格納されていることも、この値がメモリに格納されていることも意味していません。


Cでの例を示すと、2D配列int foo[6][7];があり、その要素にfoo[1][2]でアクセスする場合、fooは配列を保持する変数です。このコンテキストでfooを使用すると、配列の最初の要素へのポインターに変わります。このポインタはどの変数にも格納されておらず、メモリにも格納されていません。その値はCPUのレジスタ内でのみ生成され、使用されてから忘れられます。同様に、式foo[1]は、このコンテキストでは別のポインターに変わります。これも、変数にはなく、メモリには格納されませんが、CPUで計算され、使用され、忘れられます。 3つの概念variablememory locationおよびpointerは、実際には3つの異なる概念です。


ところで、私は「各メモリ位置に常に正確に1バイトが格納されている」ことを本当に意味していました。これは50年前のコンピューティングの石器時代には当てはまりませんでしたが、今日使用されているほとんどすべてのハードウェアに当てはまります。 1バイトより大きい値をメモリに格納するときは常に、実際にはいくつかの連続したメモリ位置を使用しています。つまり(ビッグエンディアンのバイト順を想定)数値0x01234567は、次のようにメモリに格納されます。

+------+------+------+------+
| 0x01 | 0x23 | 0x45 | 0x67 |
+------+------+------+------+
    ^      ^      ^      ^
    |      |      |      |
 0x4242 0x4243 0x4244 0x4245

(X86アーキテクチャーのようなリトルエンディアンマシンは、バイトを逆の順序で格納します。)これはポインターにも当てはまります。64ビットマシンのポインターは、それぞれ独自のメモリアドレスを持つ8つの連続したバイトに格納されます。 メモリセルを見て、「これはポインタです!」と言うことはできません。メモリを見ると、常にバイトだけが表示されます

これらの2つのステートメントを比較するとき、実際の質問に焦点を当てましょう-"who's right?"

  • ポインタは、値が別の変数のメモリアドレスである変数です。
  • ポインタは、値が別のメモリロケーションのメモリアドレスであるメモリロケーションです。

これに対する答えはnoneです。最初のものは「別の変数のメモリアドレス」について話しますが、他の答えがすでに説明したように、変数は必ずしもメモリアドレスを持つ必要はありません。 2つ目は「ポインタはメモリの場所です」とありますが、ポインタは文字どおり単なる数値であり、変数に格納できますが、以前と同様に、変数には必ずしもメモリアドレスがありません。

より正確なステートメントの例:

  • 「ポインタは、メモリ位置のメモリアドレスを表す数値です」、または

  • 「ポインタ変数は、値がメモリロケーションのメモリアドレスである変数です。」

  • 「メモリアドレスは、メモリロケーションのメモリアドレスを表すポインタを保持できます。」

「ポインター」という用語が「ポインター変数」のショートカットとして使用される場合があることに注意してください。混乱を招かない限り問題ありません。

5
Doc Brown

確かに、ポインタがアドレスを含むメモリ位置であるとは言いません。まず、私は0x23453は1バイトに収まります。 :)バイト/ワードの区別をハンドウェーブしたとしても、everyのメモリロケーションにアドレスが含まれているという問題があります。アドレスは単なる数字であり、メモリの内容は単なる数字です。

ここでの秘訣は、「ポインタ」がアーキテクチャの特定の機能ではなく、人間の意図を記述することだと思います。これは、「文字」または「文字列」がメモリで確認できる具体的なものではないのと似ています。これらもすべて単なる数値ですが、文字列として機能するため、文字列として機能します。 「ポインタ」は単にアドレスとして使用されることを意図した値を意味します。

正直なところ、あなたの目標が特定の言語(Objective C?)を教えることである場合、古典的なメモリテープを引き出すことがそれだけでも役立つとは思いません。型付きの値と1バイトでは大きすぎる値を表示することで、すでに白い嘘をついています。メカニズムではなくセマンティクスを教える—ポインターに関する重要な洞察は、ポインターがindirectionを提供することです。これは、理解するのに非常に役立つツールです。

whereにいくつかのデータを見つけるように指示しますが、それ自体はデータではありません。聞いてください:

  • 実際にURLが何であるかは気にしません;それらの大部分は、名前との関連で離れてリスにされています。多くの人が、URLがページにどのようにするかを知らずにインターネットを使用しています。一部の人々は完全にURLに気づいていません。

  • すべての文字列がURLであるとは限らず、URLとして使用されることも意図されていません。

  • 偽のURL、または以前は存在していたが削除されたページにアクセスしようとすると、エラーが発生します。

  • URLは、画像、テキスト、音楽、またはその他の任意の数の個別のアイテムを指す場合があります。あるいは、さまざまなものが含まれるページを指す場合もあります。レイアウトは似ているがデータが異なるページがたくさんあることは非常に一般的です。

  • Webページを作成し、他のWebページ上のデータを参照したい場合は、それをすべてコピーして貼り付ける必要はありません。あなたはそれにリンクを張ることができます。

  • 他のページはいくつでも同じURLにリンクできます。

  • 同様のページのコレクションがある場合は、それらすべてへのリンクを一覧表示するインデックスページを作成するか、ページ1の下部にページ2に移動する "次の"リンクを配置する、などとすることができます。両方のアプローチの長所と短所はすぐに明らかです。特に、さまざまな場所でページを追加または削除するためにWebマスターが何をする必要があるかを考えると、.

このアナロジーはそれを非常に明確にしますポインタはforを理解するために重要です—そうでなければ、それらは恣意的で複雑に見えるだけです、そして無意味です。何かがどのように機能するかを理解することは、それが何をするのか、なぜそれが役立つのかをすでに理解していれば、はるかに簡単です。ポインタが他のどこにあるかを示すブラックボックスであることをすでに内部化している場合、その後メモリモデルの複雑さについて学習します。 、ポインタをアドレスとして表すことはかなり明白です。さらに、セマンティクスを教えることで、他の間接的な形式を理解および発明するためのはるかに優れた場所に生徒を置くことができます。これは、ほとんどの主要言語にポインタがない場合に適しています。

5
Eevee

私はあなたがすでに回答を受け入れていることを知っています。この質問にはすでに5つの回答がありますが、彼らが言及していない点があります。 CSの教科書では、プログラミング言語の選択にとらわれないことがよくあります。これは、物事の説明に使用される用語が普遍的であるという暗黙の仮定につながります。そうではありません。

Cでは、単項アンパサンド演算子は「アドレス」演算子と呼ばれます。 Cプログラマは、式&xが変数xのアドレスに評価されることをためらうことはありません。もちろん、これらは「変数xの値が格納されるメモリアドレス」を意味しますが、何気ない会話では誰もそのような知識を持っています。 Cでは、Wordの「ポインタ」は通常、値としてメモリアドレスを持つことを目的とした変数のデータ型を指します。または同等に値のデータ型。しかし、一部の人々は「ポインタ」を値自体として使用します。

Javaでは、オブジェクト型または配列型のすべての変数はCポインターのように動作しますが(ポインター演算を除く)、Javaプログラマーはポインターではなく参照を呼び出します。

C++は、参照とポインタを異なる概念と見なしています。それらは関連していますが、まったく同じではないため、C++プログラマーは会話で区別する必要があります。アンパサンドは、一部のコンテキストでは「address-of」、別のコンテキストでは「reference-to」として読み取られます。

ポインタは、値が別の変数のメモリアドレスである変数です。

これは、Cプログラマーが「ポインター」を「int」と同じ意味で使用して説明する方法です。 (のように、「ポインターはメモリアドレスを保持し、intは特定の範囲内の整数を保持します。」)

ポインタは、値が別のメモリロケーションのメモリアドレスであるメモリロケーションです。

「is」の非常に緩い非公式な定義が必要なため、これは奇妙な言い方です。

変数はメモリ位置と同じであると言っても安全ですか?

メモリアドレスは、変数の値が格納されるメモリ内の場所であると言う方が明確です。 (当然のことながら、コンパイラの最適化により、すべての変数がメモリに格納されるわけではありませんが、&xでアドレスが取得される変数はすべてメモリに格納されます。)

2
gatkin

ステートメントポインタは別の変数のメモリアドレスを値とする変数ですは単純化されすぎています。しかし、メモリの場所が正確に何であり、変数とどのように異なるかを読者が理解するまでに、ポインタは正確に何であるかをすでに理解しているため、この不正確な説明に頼る必要はありません。

ステートメントポインタは、値が別のメモリロケーションのメモリアドレスであるメモリロケーションですは間違っています。ポインタの値はメモリの場所に格納する必要はありません。また、ポインタがメモリの場所を指す必要があるかどうかは、「メモリ」の意図した定義に応じて、議論の余地があります。

変数とメモリ位置の違いは何ですか

メモリの場所は、データを保存できる複数の場所の1つです。そのデータは、変数または変数の一部にすることができます。変数は、データにラベルを付ける方法です。

1
Peter

この回答はCおよびC++に焦点を当てています。あなたの質問は他の言語よりもC/C++の不可欠な部分であるポインターに関係しているので、それは適切に思えます。この投稿のほとんどは、手の込んだ実行時間のないほとんどのコンパイル済み言語に適用されます(PascalやAdaのようですが、JavaまたはC#)のようではありません)。

すでに与えられた良い答えは、変数は物理メモリよりも抽象的なレベルの言語構造であることを強調しています。この抽象化には一定の根拠とシステムがあることを強調しておきます。

抽象化は主に、リテラルアドレスの代わりに名前を使用することで構成されます。

基本的な考え方は、変数は型付きオブジェクトの名前付きハンドルであるということです。 C/C++のオブジェクトは通常メモリ内にあります。次に、言語は、型変換のためのライフタイム管理とデータマーシャリングに関するいくつかの優れた点を追加します。変数の概念は、アドレスの数値やメモリ内の関数の正確な位置を実際には気にしないため、物理アドレスよりも抽象的です。単に名前を付け、後で名前でアドレスを指定するだけで、コンパイラー、リンカー、ランタイムシステムが詳細を処理します。

そして、C/C++がメモリにとらわれないふりをしないでください。結局のところ、普遍的に適用可能なアドレス演算子があります。はい、本当です。レジスタストレージクラスでC変数のアドレスを取得することはできません。しかし、最後に使用したのはいつですか?これは一般的な概念の特別な例外であり、議論の全面的な却下ではありません。逆に、一般的なルールは、変数のアドレスを取得すると、実際にはコンパイラーにメモリ内にオブジェクトを作成することを強制するということです。 「名前付きハンドル」の概念は、C++参照の優れたパラダイムでもあります。参照は、同じオブジェクトの別の名前です。

68k用のインラインアセンブラーを作成したとき、変数名をアドレスレジスタへのオフセットとして使用する方法を確認できて良かった(そして、ベアメタルレジスタ名の代わりにregisterと宣言された変数の名前を使用できる!)。コンパイラにとって、変数は一定のアドレスオフセットです。繰り返します。変数は、通常メモリ内のオブジェクトの名前付きハンドルです。

質問は、C標準に追加の保証を追加することによって形成された一般的な言語を対象としているように聞こえます。 、前の部分が優勢。」、および他の言語でのこの用語の使用と一致する「変数」の定義。

その言語では、各メモリの場所は、常にいくつかのビット(通常は8)を保持する番号付きのメールボックスと見なすことができます。メモリの場所は、通常、2、4、または8行で構成されています。一部の操作は、複数の連続したメモリ位置で同時に処理されます。マシンによっては、2つ、4つ、または8つのメモリロケーションのグループで動作する一部の操作は、単一行内のロケーションでの動作に制限される場合があります。さらに、連続番号のメールボックスが1つの部屋にあるマシンもあれば、番号の付いたメールボックスの互いに素なグループが複数あるマシンもあります。

変数は、その変数に排他的に関連付けられているメモリロケーションの範囲と、それらのメモリロケーションを解釈する必要があるタイプを識別します。変数を読み取ると、関連する格納場所内のビットが変数のタイプに適した方法で解釈され、変数を書き込むと、関連するビットがそのタイプと値に適した方法で設定されます。

アドレスは、メールボックスを識別するために必要なすべての情報をカプセル化します。これは、単純な番号として、またはそのグループ内のメールボックスの番号とともにある種のグループ指定子として格納できます。

&演算子を変数に適用すると、アドレスとそのタイプをカプセル化するポインターが生成されます。単項*または[]演算子をポインターに適用すると、カプセル化されたアドレスで始まるメールボックスのビットが、カプセル化されたタイプに適切な方法で解釈または設定されます。

0
supercat

私はこのパーティーに遅刻しますが、2セントを入れることに抵抗することはできません。

これらの時点で、これらのメモリ位置に格納されている値の違いは何ですか?

時間1

enter image description here

時間2

enter image description here

正解:なし。それらはすべて同じ意味であり、意味の解釈は異なります。

どうやってそれを知るのですか?これを作ったのは私だからです。あなたはまだそれを本当に知りません。

out of band 問題と呼ばれる問題が発生しています。これらの値の意味を正しく解釈する方法はここには保存されていません。その知識は他の場所に保存されます。しかし、紙にこれらの価値観を提示するときは、その解釈を入れます。これは、これらのメモリ位置に存在しない情報を追加したことを意味します。

たとえば、ここの値は同じですが、 [〜#〜] ascii [〜#〜] /UTF-8文字エンコーディングが [〜#〜] ebcdic [〜#〜] と言うのではなく、最初の1つを取得する方法。また、2つ目は、これらのメモリ位置に格納されている数値の16進数表現であり、すべて「0x」で始まる文字列への参照ではなく、すべて他のアドレスへのポインターであると想定する必要もあります。 :P

これらのメモリ位置に格納されているものは、それらの仮定が正しいことを示しているわけではありません。その情報を保存できます。しかし、それは別の場所に保管されます。

プレゼンテーションの問題 です。最初にそれを提示する方法に同意することなしに、あなたはまったく数を表現することはできません。仮定、規則、コンテキストに頼ることができますが、それを深く掘り下げて、プレゼンテーションが明示的に定義されていない場合、真に正しい答えは「情報が不十分」です。

0
candied_orange