ここでのいくつかの回答は、ポインターは数値であると述べています。これは、C 標準で指定されているポインターの正確な説明ではありません。
大部分は、(a) ポインター減算がバイトから (減算されるポインターの型の) 要素に違いを変換することを理解していれば、ポインターを数値およびメモリ内のアドレスと考えることができます。このモデルが壊れる限界を理解してください。
以下では、1999 C 標準 (ISO/IEC 9899、第 2 版、1999-12-01) を使用しています。以下は質問者が要求したよりも詳細であると思いますが、ここにいくつかの虚偽の記載があることを考えると、正確かつ正確な情報を提供する必要があると判断します.
6.5.6 パラグラフ 9 に従って、同じ配列の要素または配列の最後の要素の 1 つ後ろを指す 2 つのポインターを減算できます。したがって、 がある場合int a[8], b[4];
、a[5] と a[2] は同じ配列の要素であるため、a[2] へのポインターから a[5] へのポインターを減算できます。a[8] は配列の最後の要素の 1 つ後ろにあるため、a[8] へのポインターから a[5] へのポインターを減算することもできます。(a[8] は配列にありません。a[7] は最後の要素です。) b[2] へのポインターから a[5] へのポインターを減算することはできません。 b[2] と同じ配列。または、より正確には、このような減算を行う場合、動作は未定義です。指定されていないのは結果だけではないことに注意してください。結果として無意味な数値が得られるとは期待できません。未定義です。C 標準によれば、これは、結果として何が起こるかについて C 標準が何も述べていないことを意味します。あなたのプログラムはあなたに妥当な答えを与えるか、中断するか、ファイルを削除する可能性があり、それらの結果はすべて C 標準に準拠しています。
許可された減算を行うと、結果は、2 番目に指定された要素から最初に指定された要素までの要素の数になります。したがって、a[5]-a[2]
は 3 であり、a[2]-a[5]
-3 です。これは、タイプに関係なく当てはまりますa
。C 実装では、距離をバイト (または使用する単位) から適切な型の要素に変換する必要があります。a
がそれぞれ 8 バイトの double の配列である場合a[5]-a[2]
、3 要素の場合は 3 です。a
がそれぞれ 1 バイトの char の配列である場合a[5]-a[2]
、3 要素の場合は 3 です。
ポインターが単なる数値ではないのはなぜですか? 一部のコンピューター、特に古いコンピューターでは、メモリのアドレス指定がより複雑でした。初期のコンピューターのアドレス空間は小さかった。製造業者がより大きなアドレス空間を作りたいと思ったとき、古いソフトウェアとの互換性を維持したいとも考えていました。また、ハードウェアの制限により、メモリをアドレス指定するためのさまざまなスキームを実装する必要がありました。これらのスキームには、メモリとディスクの間でデータを移動したり、アドレスを物理メモリ位置に変換する方法を制御するプロセッサ内の特殊なレジスタを変更したりする必要があった可能性があります。そのようなマシンでポインターが機能するには、単純なアドレスだけでなく、より多くの情報が含まれている必要があります。このため、C 標準では、ポインターをアドレスとして定義するだけでなく、アドレスに対して演算を行うことができます。
最新のマシンでも、複雑な問題が発生する可能性があります。Digital の Alpha プロセッサでは、関数へのポインタに関数のアドレスが含まれていません。関数の記述子のアドレスです。その記述子には関数のアドレスが含まれており、関数を正しく呼び出すために必要な追加情報が含まれています。
などの関係演算子に関して>
、C 標準では、6.5.8 パラグラフ 5 で、上記のように、減算できる同じポインターを比較することができ、ポインターを集約オブジェクトのメンバー (a構造体または共用体)。配列のメンバー (またはその終了アドレス) へのポインターは、予想どおりに比較されます。インデックスの大きい要素へのポインターは、インデックスの小さい要素へのポインターよりも大きくなります。同じ共用体の 2 つのメンバーへのポインターは等しいと比較されます。構造体の 2 つのメンバーへのポインターの場合、後で宣言されたメンバーへのポインターは、以前に宣言されたメンバーへのポインターよりも大きくなります。
上記の制約内にとどまる限り、ポインターはメモリアドレスである数値と考えることができます。
通常、C 実装が C 標準で必要な動作を提供するのは簡単です。コンピューターにベース アドレスとオフセットなどの複合ポインター スキームがある場合でも、通常、配列のすべての要素は互いに同じベース アドレスを使用し、構造体のすべての要素は互いに同じベース アドレスを使用します。そのため、コンパイラは単にポインタのオフセット部分を減算または比較して、目的の差異または比較を取得できます。
ただし、そのようなコンピューターで異なる配列へのポインターを減算すると、奇妙な結果が得られる可能性があります。ベース アドレスとオフセットによって形成されるビット パターンは、別のポインターがメモリ内の下位アドレスを指している場合でも、(単一の整数として解釈される場合) よりも大きく見える可能性があります。これが、C 標準によって設定されたルール内にとどまらなければならない理由の 1 つです。