ポインター(コンピュータープログラミング)

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

私は、代入ステートメントとポインター変数は、コンピューターサイエンスの「最も価値のある宝物」の1つであると考えています。

Donald Knuth構造化プログラミング、ステートメント[1]に移動

変数bに関連付けられたメモリアドレスを指すポインタこの図では、コンピューティングアーキテクチャは、ポインタと非ポインタの両方に同じアドレス空間データプリミティブを使用しています。この必要性は当てはまらないはずです。

コンピュータサイエンスではポインタメモリアドレスを格納する多くのプログラミング言語のオブジェクトです。これは、コンピュータメモリにある別の値の場合もあれば、メモリマップドコンピュータハードウェアの値の場合もあります。ポインタはメモリ内の場所を参照し、その場所に格納されている値を取得することを間接参照と呼びます。 ポインタ。例えとして、本の索引のページ番号は、対応するページへのポインタと見なすことができます。このようなポインタの間接参照は、指定されたページ番号のページにめくり、そのページにあるテキストを読み取ることによって行われます。ポインタ変数の実際の形式と内容は、基盤となるコンピュータアーキテクチャによって異なります。

ポインタを使用すると、反復可能なデータ構造文字列ルックアップテーブル制御テーブルツリー構造など)のトラバースなど、反復操作のパフォーマンスが大幅に向上します。特に、ポインタが指すデータをコピーしてアクセスするよりも、ポインタをコピーして逆参照する方が時間とスペースの点ではるかに安価であることがよくあります。

ポインタは、手続き型プログラミングで呼び出されたサブルーチンのエントリポイントのアドレスを保持するため、およびダイナミックリンクライブラリ(DLL)への実行時リンクのためにも使用されますオブジェクト指向プログラミングでは、関数へのポインタはメソッドのバインドに使用され、多くの場合、仮想メソッドテーブルを使用します。

ポインタは、より抽象的な参照 データ型の単純でより具体的な実装です。いくつかの言語、特に低水準言語は、ある種のポインターをサポートしていますが、他の言語よりも使用制限が多い言語もあります。 「ポインタ」は一般的に参照を参照するために使用されてきましたが、インターフェイスがポインタの操作を明示的に許可するデータ構造に、より適切に適用されます算術的に魔法のクッキーそれを許可しない機能対照的に、メモリアドレスとしてのポインタ演算)。 [要出典]ポインタはメモリアドレスへの保護されたアクセスと保護されていないアクセスの両方を許可するため、特に後者の場合、ポインタの使用に関連するリスクがあります。整数に似た形式で格納されます。ただし、値が有効なメモリアドレスではないポインタを逆参照または「ルックアップ」しようとすると、プログラムがクラッシュ(または無効なデータが含まれる)可能性があります。型安全性の問題として、この潜在的な問題を軽減するため、ポインタは、基になる表現が整数であっても、ポインタが指すデータの型によってパラメータ化された別個の型と見なされます。ポインタ変数に有効なメモリアドレスであり、プロセッサがアドレス指定できる数値範囲内の値を含んでいることを確認するために、 他の手段(検証境界チェックなど)を実行することもできます。

歴史

1955年、ソビエトのコンピューター科学者であるカテリナユシチェンコは、ポインターに類似した、間接アドレス指定と最高ランクのアドレスを可能にするアドレスプログラミング言語を発明しました。この言語は、ソビエト連邦のコンピューターで広く使用されていました。しかし、それはソビエト連邦の外では知られておらず、通常、ハロルド・ローソンは1964年にポインターの発明をしたとされています。[2] 2000年、LawsonはIEEEからComputer Pioneer Awardを授与されました。「[f]またはポインタ変数を発明し、この概念をPL / Iに導入して、リンクリストを柔軟に処理する機能を初めて提供しました。汎用高級言語」。[3]概念に関する彼の独創的な論文は、CACMの1967年6月号に「PL / Iリスト処理」というタイトルで掲載されました。Oxford English Dictionaryによると単語 ポインタは、 System DevelopmentCorporationによる技術メモのスタックポインタとして最初に印刷されました

正式な説明

コンピュータサイエンスでは、ポインタは一種の参照です。

データプリミティブ(または単にプリミティブ)は、1回のメモリアクセスを使用してコンピュータメモリから読み取りまたはコンピュータメモリに書き込むことができる任意のデータですたとえば、バイトワードの両方がプリミティブです)。

データアグリゲート(または単にアグリゲート)は、メモリ内で論理的に隣接し、1つのデータとして集合的に表示されるプリミティブのグループです(たとえば、アグリゲートは3つの論理的に連続したバイトであり、その値は空間内のポイント)。アグリゲートが完全に同じタイプのプリミティブで構成されている場合、アグリゲートは配列と呼ばれることがあります。ある意味で、マルチバイトワードプリミティブはバイトの配列であり、一部のプログラムはこのようにワードを使用します。

これらの定義のコンテキストでは、バイトは最小のプリミティブです。メモリアドレスは異なるバイトを指定します。データムの最初のバイトのメモリアドレスは、データム全体 のメモリアドレス(またはベースメモリアドレス)と見なされます。

メモリポインタ(または単にポインタ)はプリミティブであり、その値はメモリアドレスとして使用されることを目的としています。ポインタはメモリアドレスを指していると言われていますポインタの値がデータムのメモリアドレスである場合、ポインタは [メモリ内の]データムを指すとも言われます。

より一般的には、ポインタは一種の参照であり、ポインタはメモリ内のどこかに格納されているデータを参照すると言われています。そのデータを取得するには、ポインタを逆参照します。ポインタを他の種類の参照から分離する機能は、ポインタの値がメモリアドレスとして解釈されることを意図していることです。これは、かなり低レベルの概念です。

参照は間接参照のレベルとして機能します。ポインタの値によって、計算で使用されるメモリアドレス(つまり、どのデータム)が決まります。間接参照はアルゴリズムの基本的な側面であるため、ポインターはプログラミング言語では基本的なデータ型として表現されることがよくあります。静的にまたは強く)型付けされたプログラミング言語では、ポインターのによって、ポインターが指すデータ型の型が決まります。

建築のルーツ

ポインタは、ほとんどの最新のアーキテクチャによって提供されるアドレス指定機能に加えて、非常に薄い抽象化です。最も単純なスキームでは、アドレスまたは数値インデックスがシステム内のメモリの各ユニットに割り当てられます。このユニットは通常、アーキテクチャがバイトアドレス可能ワードアドレス可能かに応じて、バイトまたはワードのいずれかになります。すべてのメモリを非常に大きな配列に効果的に変換します。次に、システムは、メモリユニットの特定のアドレスに格納されている値を取得する操作も提供します(通常、マシンの汎用レジスタを利用します)。)。

通常の場合、ポインタは、システム内のメモリの単位よりも多くのアドレスを保持するのに十分な大きさです。これにより、十分なメモリがインストールされていない(つまり、使用可能なメモリの範囲を超えている)か、アーキテクチャがそのようなアドレスをサポートしていないために、プログラムがメモリの単位に対応しないアドレスにアクセスしようとする可能性があります。最初のケースは、 Intel x86アーキテクチャなどの特定のプラットフォームでは、セグメンテーション違反(segfault)と呼ばれる場合があります。 2番目のケースは、AMD64の現在の実装で可能であり、ポインターは64ビット長で、アドレスは48ビットまでしか拡張されません。ポインタは特定のルール(正規アドレス)に準拠している必要があるため、非正規ポインタが逆参照されると、プロセッサは一般保護違反

一方、一部のシステムには、アドレスよりも多くのメモリユニットがあります。この場合、メモリのセグメンテーションページングなどのより複雑なスキームを使用して、メモリのさまざまな部分をさまざまな時間に使用します。x86アーキテクチャの最後の化身は、最大36ビットの物理メモリアドレスをサポートします。これらのアドレスは、PAEページングメカニズムを介して32ビットの線形アドレス空間にマップされましたしたがって、一度にアクセスできるのは、可能な合計メモリの1/16のみです。同じコンピュータファミリの別の例は、80286の16ビットプロテクトモードでした。プロセッサは、16 MBの物理メモリしかサポートしていませんが、最大1 GBの仮想メモリにアクセスできますが、16ビットアドレスとセグメントレジスタの組み合わせにより、1つのデータ構造で64KBを超えるアクセスが煩雑になりました。

一貫性のあるインターフェイスを提供するために、一部のアーキテクチャはメモリマップドI / Oを提供します。これにより、一部のアドレスはメモリの単位を参照し、他のアドレスはコンピュータ内の他のデバイスのデバイスレジスタ参照できます。ファイルオフセット、配列インデックス、リモートオブジェクト参照など、他のタイプのオブジェクトのアドレスと同じ目的のいくつかに役立つ類似の概念があります。

使用し

ポインタは、 PL / ICC ++PascalFreeBASICなどの言語では制限なしに直接サポートされ、ほとんどのアセンブリ言語では暗黙的にサポートされます。これらは主に参照を作成するために使用されます。参照は、ほぼすべてのデータ構造を作成するための基本であり、プログラムのさまざまな部分間でデータを渡す場合にも使用されます。

リストに大きく依存する関数型プログラミング言語では、データ参照は、consや対応する要素carやcdrなどのプリミティブ構造を使用して抽象的に管理されます。これは、consセルの1番目と2番目のコンポーネントへの特殊なポインターと考えることができます。これにより、関数型プログラミングの慣習的な「フレーバー」が生まれます。このようなcons-listsでデータを構造化することにより、これらの言語は、データを構築および処理するための再帰的手段を促進します。たとえば、リストのリストの先頭要素と末尾要素に再帰的にアクセスします。例:「cdrのcdrの車に乗る」。対照的に、配列の近似におけるポインタの逆参照に基づくメモリ管理メモリアドレスの数は、変数をデータを必須に割り当てることができるスロットとして扱うのを容易にします

配列を処理する場合、重要なルックアップ操作には通常、アドレス計算と呼ばれる段階が含まれます。この段階では、配列内の目的のデータ要素へのポインターを作成します。リンクリストなどの他のデータ構造では、構造の1つの部分を別の部分に明示的に結び付けるための参照としてポインタが使用されます。

ポインタは、参照によってパラメータを渡すために使用されます。これは、プログラマーがパラメーターに対する関数の変更を関数の呼び出し元に表示したい場合に役立ちます。これは、関数から複数の値を返す場合にも役立ちます。

ポインタを使用して、メモリ内の動的変数と配列の割り当てと割り当て解除を行うこともできます。変数は、その目的を果たした後に冗長になることが多いため、変数を保持するのはメモリの浪費です。したがって、不要になったときに(元のポインタ参照を使用して)変数の割り当てを解除することをお勧めします。そうしないと、メモリリークが発生する可能性があります(使用可能な空きメモリが徐々に、または深刻な場合は急速に、多数の冗長メモリブロックの蓄積により減少します)。

Cポインタ

ポインタを定義するための基本的な構文は次のとおりです。 [4]

int * ptr ; 

ptrこれは、次のタイプのオブジェクトの識別子として 宣言します。

  • タイプのオブジェクトを指すポインタ int

これは通常、「ptrはへのポインタ」としてより簡潔に述べられていますint

C言語では、自動保存期間のオブジェクトの暗黙的な初期化が指定されていないため、[5]ptrポイントが有効なアドレスを確認するように注意する必要があります。これが、ポインタをnullポインタ値に明示的に初期化することが提案される場合がある理由です。nullポインタ値は、従来、標準化されたマクロを使用してCで指定されていましたNULL[6]

int * ptr = NULL ;   

Cでnullポインターを逆参照すると、未定義の動作[7]が発生し、壊滅的な動作を引き起こす可能性があります。ただし、ほとんどの実装[要出典]は、問題のプログラムの実行を停止するだけで、通常はセグメンテーション違反が発生します。

ただし、ポインタを不必要に初期化すると、プログラムの分析が妨げられ、バグが隠される可能性があります。

いずれにせよ、ポインタが宣言されると、次の論理的なステップは、ポインタが何かを指すようにすることです。

int a = 5 ;   
int * ptr = NULL ;   

ptr = &a ;  

これにより、のアドレスの値がに割り当てaられptrます。たとえば、aが0x8130のメモリ位置に格納されている場合ptr、割り当て後のの値は0x8130になります。ポインターを逆参照するために、アスタリスクが再度使用されます。

* ptr = 8 ;  

これは、(0x8130)の内容を取得し、ptrそのアドレスをメモリに「配置」して、その値を8に設定することを意味します。a後で再度アクセスすると、新しい値は8になります。

この例は、メモリを直接調べるとより明確になる可能性があります。aこれがメモリ内のアドレス0x8130と0x8134にあると仮定しptrます。また、これが32ビットマシンであり、intが32ビット幅であると想定します。以下は、次のコードスニペットが実行された後にメモリにあるものです。

int a = 5 ;   
int * ptr = NULL ;   
住所 コンテンツ
0x8130 0x00000005
0x8134 0x00000000

(ここに示されているNULLポインターは0x00000000です。)のアドレスをa割り当てることによってptr

 ptr = &a ;  

次のメモリ値が生成されます。

住所 コンテンツ
0x8130 0x00000005
0x8134 0x00008130

次に、ptrコーディングによる間接参照によって:

 * ptr = 8 ;  

コンピュータはptr(0x8130)の内容を取得し、そのアドレスを「検索」し、その場所に8を割り当てて、次のメモリを生成します。

住所 コンテンツ
0x8130 0x00000008
0x8134 0x00008130

明らかに、前の命令がポインタを介しaての内容を変更したため、アクセスすると値8が生成されますaptr

データ構造での使用

リストキュー、ツリーなどのデータ構造を設定する場合、構造の実装と制御の方法を管理するのに役立つポインターが必要です。ポインターの典型的な例は、開始ポインター、終了ポインター、およびスタックポインターです。これらのポインタは、絶対(実際の物理アドレスまたは仮想メモリ内の仮想アドレス)または相対(絶対開始アドレス(「ベース」)からのオフセット)のいずれかであり、通常は完全なアドレスよりも少ないビットを使用しますが、通常は1つ追加する必要があります解決する算術演算)。

相対アドレスは手動メモリセグメンテーションの形式であり、その長所と短所の多くを共有しています。 16ビットの符号なし整数を含む2バイトのオフセットを使用して、最大64 KiB(2 16バイト)のデータ構造の相対アドレス指定を提供できます。これは、ポイントされたアドレスがハーフワード、ワード、またはダブルワードの境界に強制的に整列される場合、128、256、または512 KiBに簡単に拡張できます(ただし、追加の「左シフト」ビット演算が必要です)—ベースアドレスに追加する前に、オフセットを2、4、または8倍に調整するために、1、2、または3ビットずつ)。ただし、一般的に、このようなスキームは多くの問題を抱えており、プログラマーの便宜のために絶対アドレス(およびその基礎となるフラットアドレス空間)が推奨されます。

文字の16進ASCII値(X'29 'など)などの1バイトのオフセットを使用して、配列(X'01'など)内の代替整数値(またはインデックス)を指すことができます。このようにして、文字を「生データ」から使用可能なシーケンシャルインデックスに、そしてルックアップテーブルなしで絶対アドレスに非常に効率的に変換できます

C配列

Cでは、配列のインデックス付けは、ポインター演算の観点から正式に定義されています。つまり、言語仕様では、array[i]と同等である必要があります*(array + i)[8]したがって、Cでは、配列は(ギャップのない)メモリの連続領域へのポインタと考えることができ[8]、配列にアクセスするための構文は、ポインタの逆参照に使用できる構文と同じです。たとえば、配列arrayは次の方法で宣言および使用できます。

int配列[ 5 ]; / * 5つの連続する整数を宣言します* /       
int * ptr =配列; / *配列はポインタとして使用できます* /     
ptr [ 0 ] = 1 ; / *ポインタは配列構文でインデックスを付けることができます* /          
* 配列+ 1 = 2 ; / *配列はポインタ構文で逆参照できます* /      
* 1 +配列= 2 ; / *ポインタの追加は可換です* /      
配列[ 2 ] = 4 ; / *添え字演算子は可換です* /        

これにより、5つの整数のブロックが割り当てられ、ブロックに名前が付けられますarray。これは、ブロックへのポインターとして機能します。ポインタのもう1つの一般的な使用法は、 mallocから動的に割り当てられたメモリを指すことです。これにより、配列として使用できる、要求されたサイズ以上のメモリの連続ブロックが返されます。

配列とポインターのほとんどの演算子は同等ですが、sizeof演算子の結果は異なります。この例でsizeof(array)は、は5*sizeof(int)(配列のサイズ)にsizeof(ptr)評価されsizeof(int*)、は、ポインタ自体のサイズに評価されます。

配列のデフォルト値は、次のように宣言できます。

int array [ 5 ] = { 2、4、3、1、5 } ; _ _ _ _ _ _ _       

arrayが32ビットリトルエンディアンマシンのアドレス0x1000から始まるメモリにある場合、メモリには次のものが含まれます(値はアドレスと同様 に16進数です)。

0 1 2 3
1000 2 0 0 0
1004 4 0 0 0
1008 3 0 0 0
100C 1 0 0 0
1010 5 0 0 0

ここに示されているのは、2、4、3、1、および5の5つの整数です。これらの5つの整数は、それぞれ32ビット(4バイト)を占め、最下位バイトが最初に格納され(これはリトルエンディアンのCPUアーキテクチャです)、連続して格納されます。アドレス0x1000から開始します。

ポインタを使用したCの構文は次のとおりです。

  • array 0x1000を意味します。
  • array + 10x1004を意味します。「+ 1」は、1のサイズint(4バイト)を追加することを意味します。
  • *arrayの内容を逆参照することを意味しarrayます。内容をメモリアドレス(0x1000)と見なし、その場所(0x0002)で値を検索します。
  • array[i]は要素番号を意味iし、0から始まり、arrayその番号はに変換され*(array + i)ます。

最後の例は、のコンテンツにアクセスする方法ですarrayそれを分解する:

  • array + iの(i)番目の要素のメモリ位置であり、arrayi = 0から始まります。
  • *(array + i) そのメモリアドレスを取得し、それを逆参照して値にアクセスします。

Cリンクリスト

以下は、 C でのリンクリストの定義例です。


/ *空のリンクリストはNULL *またはその他の番兵値で表されます* /
#define EMPTY_LIST NULL

構造体 リンク{ 
    void * data ; / *このリンクのデータ* /          
    struct  link * next ; / *次のリンク; ない場合はEMPTY_LIST * /   
};

このポインター再帰的定義は、 Haskellプログラミング言語の参照再帰的定義と本質的に同じです。

 データ リンク a  =  Nil 
             |  短所 a  リンク a 

Nilは空のリストであり、これもタイプの別のリンクを持つタイプCons a (Link a)consセルですaa

ただし、参照を含む定義は型チェックされており、混乱を招く可能性のあるシグナル値を使用していません。このため、Cのデータ構造は通常、ラッパー関数を介して処理されます。ラッパー関数は、正確性が慎重にチェックされます。

ポインタを使用したアドレス渡し

ポインタを使用して変数をアドレスで渡すことができ、値を変更できます。たとえば、次のCコード について考えてみます。

/ * int nのコピーは、呼び出し元のコードに影響を与えることなく、関数内で変更できます* /
void passByValue int n {   
    n = 12 ;  
}

/ *代わりにポインタmが渡されます。mが指す値のコピーは作成されません* /
void passByAddress int * m {   
    * m = 14 ;  
}

int main void {  
    int x = 3 ;   

    / * xの値のコピーを引数として渡します* /
    passByValue x );
    //値は関数内で変更されましたが、xはこれからも3のままです

    / * xのアドレスを引数として渡します* /
    passByAddress x );
    // xは実際には関数によって変更され、ここでは14に等しくなります

    0を返す; 
}

動的メモリ割り当て

一部のプログラムでは、必要なメモリはユーザーが入力できる内容によって異なります。このような場合、プログラマはメモリを動的に割り当てる必要があります。これは、変数が通常格納されるスタックではなくヒープにメモリを割り当てることによって行われます(変数はCPUレジスタに格納することもできますが、それは別の問題です)。動的メモリ割り当てはポインタを介してのみ行うことができ、名前(一般的な変数の場合と同様)を指定することはできません。

ポインタは、動的に割り当てられたメモリブロックのアドレスを格納および管理するために使用されます。このようなブロックは、データオブジェクトまたはオブジェクトの配列を格納するために使用されます。ほとんどの構造化されたオブジェクト指向言語は、ヒープまたはフリーストアと呼ばれるメモリ領域を提供し、そこからオブジェクトが動的に割り当てられます。

以下のCコードの例は、構造体オブジェクトが動的に割り当てられ、参照される方法を示しています。標準Cライブラリmalloc()は、ヒープからメモリブロックを割り当てるための関数を提供します。パラメータとして割り当てるオブジェクトのサイズを取得し、オブジェクトの格納に適した新しく割り当てられたメモリブロックへのポインタを返します。割り当てに失敗した場合は、nullポインタを返します。

/ *部品在庫アイテム* /
構造体 アイテム{ 
    int id ; / *部品番号* /              
    char * name ; /* 部品名 */          
    フロートコスト; /* 料金 */          
};

/ *新しいItemオブジェクトを割り当てて初期化します* /
struct  Item * make_item const char * name {     
    struct  Item * item ;  

    / *新しいItemオブジェクトにメモリブロックを割り当てます* /
    item = malloc sizeof struct Item ));   
    if item == NULL    
        NULLを返します; 

    / *新しいアイテムのメンバーを初期化します* /
    memset item 0 sizeof struct Item ));   
    item- > id = -1 ;    
    アイテム->名前= NULL ;  
    アイテム->コスト= 0.0 ;  

    / *名前のコピーを新しいアイテムに保存します* /
    item- > name = malloc strlen name + 1 );    
    if item- > name == NULL {    
        無料アイテム);
        NULLを返します; 
    }
    strcpy アイテム->名前名前); 

    / *新しく作成されたItemオブジェクトを返します* /
    返品アイテム; 
}

以下のコードは、メモリオブジェクトが動的に割り当て解除される方法、つまりヒープストアまたはフリーストアに戻される方法を示しています。標準Cライブラリは、free()以前に割り当てられたメモリブロックの割り当てを解除し、それをヒープに戻すための関数を提供します。

/ * Itemオブジェクトの割り当てを解除します* /
void destroy_item struct Item * item {    
    / * nullオブジェクトポインタをチェックします* /
    if item == NULL    
        戻る;

    / *アイテム内に保存されている名前文字列の割り当てを解除します* /
    if item- > name != NULL {    
        無料アイテム->名前);
        アイテム->名前= NULL ;  
    }

    / * Itemオブジェクト自体の割り当てを解除します* /
    無料アイテム);
}

メモリマップドハードウェア

一部のコンピューティングアーキテクチャでは、ポインタを使用してメモリまたはメモリマップドデバイスを直接操作できます。

ポインタへのアドレスの割り当ては、マイクロコントローラをプログラミングする際の非常に貴重なツールです。以下は、int型のポインターを宣言し、それを16進アドレスに初期化する簡単な例です。この例では定数0x7FFFです。

int * Hardware_address = int * 0x7FFF ;    

80年代半ばには、BIOSを使用してPCのビデオ機能にアクセスするのは遅かった。表示を多用するアプリケーションは、通常、 16進定数0xB8000を80個の符号なし16ビットint値の配列へのポインターにキャストすることにより、 CGAビデオメモリに直接アクセスするために使用されていました。各値は、下位バイトのASCIIコードと、上位バイトの色で構成されていました。したがって、行5、列2の文字「A」を青に明るい白で配置するには、次のようなコードを記述します。

#define VID((unsigned short(*)[80])0xB8000)

void foo void {  
    VID [ 4 ] [ 1 ] = 0x1F00 | 'A' ;    
}

コントロールテーブルでの使用

プログラムフローを制御するために使用される制御テーブルは、通常、ポインタを多用します。通常、テーブルエントリに埋め込まれているポインタは、たとえば、同じテーブルエントリで定義されている特定の条件に基づいて、実行されるサブルーチンへのエントリポイントを保持するために使用できます。ただし、ポインタは、実際のアドレスの配列またはアドレス自体(使用可能なプログラミング言語の構成に応じて)を含む、他の個別の、しかし関連付けられたテーブルへの単純なインデックスにすることができます。また、(ループ処理のように)以前のテーブルエントリを指すために使用したり、(スイッチのように)一部のテーブルエントリをスキップするために転送したりするために使用することもできます。またはループからの「早期」終了)。この後者の目的のために、「ポインタ」は単にテーブルエントリ番号自体であり、単純な算術によって実際のアドレスに変換することができます。

入力されたポインタとキャスト

多くの言語では、ポインタには、ポインタが指すオブジェクトが特定のタイプを持つという追加の制限がありますたとえば、ポインタは整数を指すように宣言できます。次に、言語は、プログラマーが浮動小数点数などの整数ではないオブジェクトを指すのを防ぎ、エラーを排除しようとします。

たとえば、Cでは

int *お金; 
char *バッグ; 

money整数ポインタにbagsなり、charポインタになります。次の場合、 GCCで「互換性のないポインタ型からの割り当て」というコンパイラ警告が発生します。

バッグ=お金;  

moneybagsは異なるタイプで宣言されたためです。コンパイラーの警告を抑制するには、型キャストによって実際に割り当てを行いたいことを明示する必要があります

バッグ= char * お金;   

これは、の整数ポインタをmoneycharポインタにキャストし、に割り当てることを示していbagsます。

C標準の2005ドラフトでは、あるタイプから派生したポインターを別のタイプの1つにキャストする場合、両方のタイプの位置合わせの正確さを維持する必要があります(6.3.2.3ポインター、パラメーター7):[9]

char * external_buffer = "abcdef" ;   
int * internal_data ; 

internal_data = int * external_buffer ; //「結果のポインタが//正しく整列されていない」場合の未定義動作     
                                         

ポインタ演算を許可する言語では、ポインタの演算では型のサイズが考慮されます。たとえば、ポインタに整数を追加すると、その数値に型のサイズを掛けた数だけ大きいアドレスを指す別のポインタが生成されます。これにより、上記のC配列の例に示されているように、特定のタイプの配列の要素のアドレスを簡単に計算できます。あるタイプのポインターが異なるサイズの別のタイプにキャストされる場合、プログラマーはポインター演算が異なる方法で計算されることを期待する必要があります。たとえば、Cでは、money配列が0x2000で始まり、sizeof(int)4バイトであるのに対し、sizeof(char)が1バイトである場合、money + 10x2004を指しますが、bags + 10x2001を指します。キャストのその他のリスクには、「広い」データが「狭い」場所(たとえば)に書き込まれるときのデータの損失、ビットシフトbags[0] = 65537;値のときの予期しない結果、および特に符号付きと符号なしの値の比較の問題が含まれます。

一般に、コンパイル時にどのキャストが安全であるかを判断することは不可能ですが、一部の言語は、これらの危険なキャストが実行時に有効であることを確認するために使用できる実行時型情報を格納します。他の言語は、安全なキャストの控えめな近似を受け入れるか、まったく受け入れません。

ポインタの値

CおよびC ++では、ポインター間の比較の結果は定義されていません。これらの言語 LLVMでは、ルールは「2つのポインターが同じアドレスを指しているからといって、それらが等しく、交換可能に使用できることを意味するわけではない」という意味で解釈され、ポインター間の違いは来歴と呼ばれます。[10]uintptr_t比較を提供するなどの整数型にキャストしますが、キャスト自体は実装によって定義されます。さらに、バイトと算術へのさらなる変換は、ポインターの使用を追跡しようとするオプティマイザーを捨てます。これは、学術研究でまだ解明されている問題です。[11]

ポインタをより安全にする

ポインタを使用すると、プログラムは定義されていない可能性のあるオブジェクトにアクセスできます。ポインタは、さまざまなプログラミングエラーの原因となる可能性があります。ただし、ポインタの有用性は非常に高いため、ポインタなしでプログラミングタスクを実行するのは難しい場合があります。その結果、多くの言語は、ポインターのいくつかの落とし穴(ポインターハザードとも呼ばれる)なしで、ポインターの便利な機能のいくつかを提供するように設計された構造を作成しました。このコンテキストでは、(この記事で使用されているように)メモリを直接アドレス指定するポインタは、スマートポインタや他のバリアント とは対照的に、

ポインタの大きな問題の1つは、数値として直接操作できる限り、未使用のアドレスまたは他の目的で使用されているデータを指すようにできることです。最も含む多くの言語、関数型プログラミング言語と最近の命令型言語のようなJavaは、リファレンスのより不透明なタイプのポインタを置き換え、通常は単に参照オブジェクトのみを参照するために使用される数字などの操作されない、これを防止することができ、エラーのタイプ。配列のインデックス付けは、特殊なケースとして処理されます。

アドレスが割り当てられていないポインタは、ワイルドポインタと呼ばれます。このような初期化されていないポインタを使用しようとすると、初期値が有効なアドレスではないか、プログラムの他の部分に損傷を与える可能性があるため、予期しない動作が発生する可能性があります。その結果、多くの場合、セグメンテーションフォールトストレージ違反、またはワイルドブランチ(関数ポインターまたはブランチアドレスとして使用されている場合)が発生します。

明示的なメモリ割り当てがあるシステムでは、それが指すメモリ領域の割り当てを解除することにより、ダングリングポインタを作成することができます。このタイプのポインタは危険で微妙です。割り当て解除されたメモリ領域には、割り当て解除前と同じデータが含まれている可能性がありますが、以前のコードでは不明な無関係のコードによって再割り当ておよび上書きされる可能性があるためです。ガベージコレクションを使用する言語では、スコープ内に参照がなくなると割り当て解除が自動的に実行されるため、このタイプのエラーが防止されます。

C ++などの一部の言語は、スマートポインターをサポートします。スマートポインター、参照として機能するだけでなく、動的メモリの割り当てを追跡するのに役立つ単純な形式の参照カウント使用します。オブジェクトが一連のスマートポインタを介して間接的にそれ自体を参照する参照サイクルがない場合、これらはダングリングポインタやメモリリークの可能性を排除します。Delphi文字列は、参照カウントをネイティブにサポートします。

Rustプログラミング言語でボローチェッカーポインターの有効期間、およびnullポインターのオプション型基づく最適化が導入され、ガベージコレクションに頼ることなくポインターのバグが排除されます

特殊な種類のポインタ

値で定義される種類

ヌルポインタ

ヌルポインタはポインタが有効なオブジェクトを参照しないことを示すために予約さの値を有します。ヌルポインターは、長さが不明なリスト終わりや何らかのアクションの実行の失敗などの条件を表すために日常的に使用されます。このnullポインターの使用は、null許容型およびオプション型のNothing比較できます

ダングリングポインタ

ダングリングポインタが有効なオブジェクトを指していないと、結果的にプログラムをクラッシュさせたり、奇妙な振る舞いポインタです。パスカルまたはCプログラミング言語、具体的に初期化されていないポインタは、メモリ内の予測不可能なアドレスを指してもよいです。

次のサンプルコードは、ダングリングポインタを示しています。

int func void {  
    char * p1 = malloc sizeof char )); / *ヒープ上のある場所の(未定義の)値* /    
    char * p2 ; / *ぶら下がっている(初期化されていない)ポインタ* /        
    * p1 = 'a' ; / * malloc()がNULLを返さないと仮定すると、これは問題ありません。* /        
    * p2 = 'b' ; / *これは未定義の動作を呼び出します* /        
}

ここでp2は、メモリ内の任意の場所を指している可能性があるため、割り当て*p2 = 'b';実行すると、メモリの不明な領域が破損したり、セグメンテーション違反が発生したりする可能性があります。

ワイルドブランチ

プログラムへのエントリポイントのアドレスとしてポインタが使用されている場合、または何も返さず、初期化されていないか破損している関数の開始である場合でも、このアドレスに対して呼び出しまたはジャンプが行われると、「ワイルドブランチ」 「発生したと言われています。言い換えると、ワイルドブランチは、ワイルド(ぶら下がり)の関数ポインタです。

結果は通常予測不可能であり、ポインタが「有効な」アドレスであるかどうか、およびそのアドレスに(偶然に)有効な命令(オペコード)があるかどうかに応じて、エラーはいくつかの異なる方法で現れる可能性があります。ワイルドブランチの検出は、証拠の多くが事前に、またはブランチの場所で1つ以上の不適切な命令を実行することによってすでに破棄されている可能性があるため、最も困難で苛立たしいデバッグ演習の1つを提示する可能性があります。利用可能な場合、命令セットシミュレータは通常、ワイルドブランチが有効になる前に検出できるだけでなく、その履歴の完全または部分的なトレースも提供できます。

構造によって定義される種類

自動相対ポインタ

自動相対ポインターは、その値がポインター自体のアドレスからのオフセットとして解釈されるポインターですしたがって、データ構造に、データ構造自体の一部を指す自動相対ポインタメンバーがある場合、自動相対ポインタの値を更新しなくても、データ構造をメモリ内に再配置できます。[12]

引用された特許はまた、同じことを意味するために自己相対的ポインタという用語を使用しています。ただし、その用語の意味は他の方法で使用されています。

  • ポインタ自体のアドレスからではなく、構造体のアドレスからのオフセットを意味します。[要出典]
  • 独自のアドレスを含むポインタを意味します。これは、メモリの任意の領域で、相互にポイントするデータ構造のコレクションを再構築するのに役立ちます。[13]

ベースポインタ

ベースポインタは、その値が別のポインタの値からのオフセットであるポインタです。これを使用して、データのブロックを格納およびロードし、ブロックの先頭のアドレスをベースポインタに割り当てることができます。[14]

用途またはデータ型によって定義される種類

複数の間接参照

一部の言語では、ポインターが別のポインターを参照できるため、元の値に到達するには複数の間接参照操作が必要です。間接参照の各レベルはパフォーマンスコストを追加する可能性がありますが、複雑なデータ構造に正しい動作を提供するために必要になる場合がありますたとえば、Cでは、リストの次の要素へのポインタを含む要素に関して リンクリストを定義するのが一般的です。

構造体 要素{ 
    構造体 要素*; 
    int;            
};

構造体 要素* head = NULL ;   

この実装では、リスト全体の代理として、リストの最初の要素へのポインターを使用します。リストの先頭に新しい値が追加された場合はhead、新しい要素を指すように変更する必要があります。C引数は常に値で渡されるため、二重間接参照を使用すると、挿入を正しく実装でき、リストの先頭での挿入を処理するための特殊なケースのコードを削除するという望ましい副作用があります。

// * headに並べ替えられたリストがある
場合、以前のすべての要素の値が小さい/等しい最初の場所に//要素アイテムを挿入します。
void insert struct element ** head struct element * item {       
    構造体 要素** p ; // pはp = head ; * p != NULL ; p = * p -> next {の要素へのポインタを指します   
              
        if item- > value <= * p -> value    
            休憩;
    }
    item- > next = * p ;  
    * p =アイテム;  
}

//呼び出し元はこれを行います:
insert head item ); 

この場合、の値がの値itemよりも小さい場合head、呼び出し元の値headは新しいアイテムのアドレスに適切に更新されます。

基本的な例は、C(およびC ++)のメイン関数へのargv引数にあります。これは、プロトタイプで次のように指定されます。これは、変数自体が文字列の配列(配列の配列)へのポインターであるためです。 0番目の文字列へのポインタ(慣例によりプログラムの名前)であり、0番目の文字列の0番目の文字です。 char **argvargv*argv**argv

関数ポインタ

一部の言語では、ポインタは実行可能コードを参照できます。つまり、関数、メソッド、またはプロシージャを指すことができます。関数ポインタは、呼び出される関数のアドレスを格納します。この機能は関数を動的に呼び出すために使用できますが、ウイルスやその他の悪意のあるソフトウェア作成者のお気に入りの手法であることがよくあります。

int sum int n1 int n2 { //整数値を返す2つの整数パラメーターを持つ関数return n1 + n2 ;        
       
}

int main void {  
    int a b x y ;    
    int * fp )(int int ); // sum fp = sum ;のような関数を指すことができる関数ポインタ // fpは関数sumx = * fp )(a b );を指すようになりました //引数aおよびby = sum a b );を使用して関数sumを呼び出します。//引数aとbを使用して関数sumを呼び出します}      
                    
               
                 

バックポインタ

二重にリンクされたリストまたはツリー構造では、要素に保持されているバックポインタは、現在の要素を参照しているアイテムを「指し示します」。これらは、メモリの使用量を増やすことを犠牲にして、ナビゲーションと操作に役立ちます。

配列インデックスを使用したシミュレーション

(通常は1次元の)配列へのインデックスを使用して、ポインターの動作をシミュレートすることができます。

主に、ポインタを明示的にサポートしていないが配列をサポートしている言語場合、配列は(特定の配列のスコープ内の)メモリ範囲全体であるかのように考えて処理でき、その配列へのインデックスは同等であると考えることができます。アセンブリ言語の汎用レジスタ(個々のバイトを指しますが、実際の値は配列の先頭に相対的であり、メモリ内の絶対アドレスではありません)。配列が、たとえば、連続する16メガバイトの文字データ構造であるとすると、個々のバイト(または配列内の連続するバイトの文字列)は、31ビットの符号なしの配列の名前を使用して直接アドレス指定および操作できます。シミュレートされたポインターとしての整数(これは、上記のC配列の例と非常によく似ています)。ポインター演算は、本物のポインター演算と比較して最小限の追加オーバーヘッドで、インデックスを加算または減算することによってシミュレートできます。

上記の手法を適切な命令セットシミュレータと組み合わせて使用​​すると、ポインタをまったくサポートしない別の言語(Java /など)で任意の マシンコードまたは任意のプロセッサ/言語の中間(バイトコードをシミュレートすることも理論的には可能です。 JavaScript)。これを実現するために、最初にバイナリコードを配列の連続するバイトにロードして、シミュレータが同じ配列に含まれるメモリ内で完全に「読み取り」、解釈、およびアクションを実行できるようにします。必要に応じて、バッファオーバーフローの問題を完全に回避するために、境界チェック通常、コンパイラーに対してアクションを実行できます(そうでない場合は、シミュレーターで手動でコーディングします)。

さまざまなプログラミング言語でのサポート

エイダ

Adaは強く型付けされた言語であり、すべてのポインターが型付けされ、安全な型変換のみが許可されます。すべてのポインタはデフォルトでに初期化されnullnullポインタを介してデータにアクセスしようとすると、例外が発生します。Adaのポインタはアクセスタイプと呼ばれますAda 83はアクセスタイプの演算を許可しませんでしたが(多くのコンパイラベンダーが非標準機能として提供していましたが)、Ada95はパッケージを介してアクセスタイプの「安全な」演算をサポートしていますSystem.Storage_Elements

ベーシック

Windowsプラットフォーム用のBASICのいくつかの古いバージョンでは、文字列のアドレスを返すSTRPTR()と、変数のアドレスを返すVARPTR()がサポートされていました。 Visual Basic 5では、オブジェクトインターフェイスのアドレスを返すOBJPTR()と、関数のアドレスを返すADDRESSOF演算子もサポートされていました。これらすべての型は整数ですが、それらの値はポインター型によって保持される値と同等です。

ただし、 FreeBASICBlitzMaxなどのBASICの新しい方言には、徹底的なポインタ実装があります。 FreeBASICでは、ポインタ(Cと同等)の算術演算は、ポインタがバイト幅であるかのように扱われます。 Cのように、ポインターを逆参照することはできません。また、他のタイプのポインター間でキャストしても、警告は生成されません。 ANYvoid*ANYANYANY

dim  as  integer  f  =  257 
dim  as  any  ptr  g  =  @ f 
dim  as  integer  ptr  i  =  g 
assert * i  =  257 
assert  g  +  4  =  @ f  +  1  

CおよびC ++

CおよびC ++ではポインタはアドレスを格納する変数であり、nullにすることができます。各ポインターにはそれが指す型がありますが、ポインター型間で自由にキャストできます(ただし、関数ポインターとオブジェクトポインター間ではキャストできません)。 「voidポインタ」と呼ばれる特別なポインタ型を使用すると、任意の(非関数)オブジェクトを指すことができますが、直接逆参照できない(キャストされる)という事実によって制限されます。多くの場合、アドレス自体は、十分なサイズの整数型との間でポインターをキャストすることによって直接操作できますが、結果は実装定義であり、実際には未定義の動作を引き起こす可能性があります。以前のC標準には、十分な大きさが保証された積分型がありませんでしたが、C99uintptr_t typedef名はで定義されています<stdint.h>が、実装で指定する必要はありません。

C ++は、CポインターとC型キャストを完全にサポートしています。また、コンパイル時に意図しない危険なキャストをキャッチするのに役立つ型キャスト演算子の新しいグループもサポートしています。以来、C ++ 11C ++標準ライブラリも提供するスマートポインタをunique_ptrshared_ptrおよびweak_ptrプリミティブCポインタに対する安全な代替として、いくつかの状況で使用することができます)。C ++は、単に参照または参照型と呼ばれる、ポインターとはまったく異なる別の形式の参照もサポートします

ポインター算術、つまり、算術演算(および大きさの比較)を使用してポインターのターゲットアドレスを変更する機能は、言語標準によって制限され、単一の配列オブジェクトの範囲内(またはその直後)に留まります。それ以外の場合は、未定義の動作を呼び出します。ポインタを加算または減算すると、データ型のサイズの倍数だけポインタが移動します。たとえば、4バイト整数値へのポインタに1を追加すると、ポインタが指すバイトアドレスが4ずつインクリメントされます。これには、連続する整数配列内の次の要素を指すようにポインタをインクリメントする効果があります。多くの場合、意図した結果。void型voidのため、ポインタに対してポインタ演算を実行できません。サイズがないため、指定されたアドレスを追加することはできませんが、gccおよびその他のコンパイラは、バイト演算をvoid*非標準の拡張子として実行し、のように扱いchar *ます。

ポインター演算は、プログラマーにさまざまなタイプを処理する単一の方法を提供します。バイト単位の実際のオフセットの代わりに、必要な要素の数を加算および減算します。 (ポインターを使用したポインター演算は、定義上1char *であるため、バイトオフセットを使用sizeof(char)します。)特に、C定義は、配列の-番目の要素でa[n]ある構文が、ポイントされた要素の内容であると同等であることを明示的に宣言します。によって。これは、がと同等であることを意味したとえば配列の4番目の要素にアクセスするために書くことができますna*(a + n)a + nn[a]a[n]a[3]3[a]a

ポインタ演算は強力ですが、コンピュータのバグの原因となる可能性があります。初心者のプログラマーを混乱させ、異なるコンテキストに強制する傾向があります。式は通常の算術式またはポインター算術式であり、場合によっては一方を他方と間違えやすいものです。これに対応して、多くの最新の高レベルコンピュータ言語(Javaなど)は、アドレスを使用したメモリへの直接アクセスを許可していません。また、安全なC方言Cycloneは、ポインターに関する多くの問題に対処します。詳細については、 Cプログラミング言語を参照してください。

voidポインタまたははvoid*、一般的なポインタ型としてANSICおよびC ++でサポートされています。へのポインタvoidは、任意のオブジェクト(関数ではない)のアドレスを格納でき、Cでは、割り当て時に他のオブジェクトポインタタイプに暗黙的に変換されますが、逆参照される場合は明示的にキャストする必要があります。 「タイプに依存しないポインタ」の目的で 使用されるK&R C(ANSI Cより前)。char*

int x = 4 ;   
void * p1 = x ;   
int * p2 = p1 ; // void *は暗黙的にint *に変換されます:有効なCですが、C ++ではありませinta = * p2 ;          
   
int b = * int * p1 ; //インラインで間接参照する場合、暗黙の変換はありません     

void*C ++では、割り当てであっても、他のポインター型への暗黙的な変換は許可されていません。これは、不注意なキャストや意図しないキャストを回避するための設計上の決定でしたが、ほとんどのコンパイラは、他のキャストに遭遇したときにエラーではなく警告のみを出力します。

int x = 4 ;   
void * p1 = x ;   
int * p2 = p1 ; //これはC ++では失敗します:void * int * p3 = int * p1 ;からの暗黙の変換はありません // Cスタイルのキャストint * p4 = static_cast < int *> p1 ); // C ++キャスト                        
                  
     

C ++では、参照が指す変数のエイリアスのように動作し、型が。である変数は存在しないため、void&補完する(voidへの参照)(voidへのポインター)はありませんvoid*void

ポインタ宣言構文の概要

これらのポインター宣言は、ポインター宣言のほとんどの変形をカバーします。もちろん、トリプルポインターを持つことは可能ですが、トリプルポインターの背後にある主な原則は、すでにダブルポインターに存在します。

char cff [ 5 ] [ 5 ]; / *文字の配列の配列* /      
char * cfp [ 5 ]; / *文字へのポインタの配列* /        
char ** cpp ; / * charへのポインタへのポインタ(「ダブルポインタ」)* /          
char * cpf [ 5 ]; / *文字の配列へのポインタ* /      
char * cpF (); / * char(s)へのポインタを返す関数* /         
char * CFp )(); / * charを返す関数へのポインタ* /       
char * cfpF ())[ 5 ]; / *文字の配列へのポインタを返す関数* /   
char * cpFf [ 5 ])(); / * charを返す関数へのポインタの配列* /   

()と[]は*よりも優先されます。 [15]

C#

C#プログラミング言語では、ポインターは特定の条件下でのみサポートされます。ポインターを含むコードのブロックはすべて、unsafeキーワードでマークする必要があります。このようなブロックを実行するには、通常、より高いセキュリティ権限が必要です。構文は基本的にC ++と同じであり、ポイントされるアドレスはマネージメモリまたはアンマネージメモリのいずれかです。ただし、管理対象メモリへのポインタ(管理対象オブジェクトへのポインタ)は、fixedキーワードを使用して宣言する必要があります。これにより、ポインタがスコープ内にある間、ガベージコレクタがメモリ管理の一部としてポイントされたオブジェクトを移動できなくなり、ポインタアドレスが有効に保たれます。

これの例外は、IntPtr構造を使用することです。これは、と同等の安全に管理されint*、安全でないコードを必要としません。System.Runtime.InteropServicesこのタイプは、たとえば次 のメソッドを使用するときに返されることがよくあります。

//プロセスのアンマネージメモリから16バイトのメモリを取得し
ますIntPtrpointer  = System ランタイムInteropServices 元帥AllocHGlobal 16 );  

//割り当てられたメモリで何かをします

//割り当てられたメモリ
システムを解放しますランタイムInteropServices 元帥FreeHGlobal ポインタ);

.NET FrameworkSystemには、System.Runtime.InteropServices名前空間(クラスなど)に多くクラスとメソッドが含まれておりMarshal、.NETタイプ(たとえばSystem.String)を多くのアンマネージタイプとポインター(たとえばLPWSTR、 )との間で変換して、アンマネージコードvoid*との通信を可能にしますこのようなメソッドのほとんどは、メモリ内の任意の場所に影響を与える可能性があるため、アンマネージコードと同じセキュリティ権限要件があります。

COBOL

COBOLプログラミング言語は、変数へのポインターをサポートしています。プログラム内で宣言されたプリミティブまたはグループ(レコード)データオブジェクトは、LINKAGE SECTION本質的にポインタベースであり、プログラム内で割り当てられるメモリは、データ項目のアドレス用のスペース(通常は単一のメモリワード)のみです。プログラムのソースコードでは、これらのデータ項目は他の変数と同じように使用されますが、それらの内容は、ポインター WORKING-STORAGEを介して間接的に間接的にアクセスされます。LINKAGE

ポインティングされた各データオブジェクトのメモリスペースは、通常、外部ステートメントを使用して、またはステートメントなどの埋め込み拡張言語構造を介して動的に割り当てられますCALLEXEC CICSEXEC SQL

COBOLの拡張バージョンは、USAGE IS POINTER句で宣言されたポインタ変数も提供します。このようなポインター変数の値は、SETandSET ADDRESSステートメントを使用して確立および変更されます。

COBOLの一部の拡張バージョンは、実行可能コードのアドレスをPROCEDURE-POINTER格納できる変数も提供します

PL / I

PL / I言語は、すべてのデータ型へのポインター(構造体へのポインターを含む)、再帰マルチタスク、文字列処理、および広範な組み込み関数を完全にサポートします。 PL / Iは、当時のプログラミング言語と比較してかなり飛躍的でした。[要出典] PL / Iポインターは型指定されていないため、ポインターの間接参照または代入にキャストは必要ありません。ポインタの宣言構文はDECLARE xxx POINTER;、「xxx」という名前のポインタを宣言します。ポインターはBASED変数とともに使用されます。ベース変数は、デフォルトのロケーターを使用して宣言できます(DECLARE xxx BASED(ppp);またはなしで宣言できます(DECLARE xxx BASED;)、ここでxxxはベース変数であり、要素変数、構造体、または配列である可能性があり、pppはデフォルトのポインターです)。このような変数は、明示的なポインター参照なしでアドレス指定できます(xxx=1;、またはデフォルトのロケーター(ppp)または他のポインター(qqq->xxx=1;)への明示的な参照でアドレス指定できます。

ポインター演算はPL / I標準の一部ではありませんが、多くのコンパイラーは形式の式を許可しますptr = ptr±expressionIBM PL / IにはPTRADD、算術演算を実行するための組み込み関数もあります。ポインタ演算は常にバイト単位で実行されます。

IBM Enterprise PL / Iコンパイラーには、と呼ばれる新しい形式の型付きポインターがありHANDLEます。

D

Dプログラミング言語はCおよびC ++の派生物であり、CポインターおよびC型キャストを完全にサポートします。

エッフェル

Eiffelオブジェクト指向言語は、ポインター演算なしで値と参照のセマンティクスを採用しています。それにもかかわらず、ポインタクラスが提供されます。これらは、ポインタ演算、型キャスト、明示的なメモリ管理、Eiffel以外のソフトウェアとのインターフェース、およびその他の機能を提供します。

Fortran

Fortran-90では、強く型付けされたポインター機能が導入されました。 Fortranポインターには、単なるメモリアドレス以上のものが含まれています。また、配列の次元の下限と上限、ストライド(たとえば、任意の配列セクションをサポートするため)、およびその他のメタデータをカプセル化します。関連付け演算子は、属性を持つ変数にaを関連付けるために=>使用されます。 Fortran-90ステートメントを使用して、ポインターをメモリーのブロックに関連付けることもできます。たとえば、次のコードを使用して、リンクリスト構造を定義および作成できます。 POINTERTARGETALLOCATE

type real_list_t 
  real  ::  sample_data 100 
  type  real_list_t )、 pointer  ::  next  =>  null  ()
end type

タイプ real_list_t )、 ターゲット ::  my_real_list
タイプ real_list_t )、 ポインター ::  real_list_temp

real_list_temp  =>  my_real_list 
do 
  read  1 iostat = ioerr  real_list_temp sample_data 
  if  ioerr  / =  0  exit
  割当 real_list_temp next 
  real_list_temp  =>  real_list_temp next 
end do

Fortran-2003は、プロシージャポインタのサポートを追加します。また、C相互運用性機能の一部として、Fortran-2003は、CスタイルのポインターをFortranポインターに変換したり戻したりするための組み込み関数をサポートしています。

に移動

Goにはポインタがあります。その宣言構文はCの宣言構文と同等ですが、逆に記述され、型で終わります。Cとは異なり、Goにはガベージコレクションがあり、ポインタ演算を許可していません。C ++のような参照型は、存在しません。マップやチャネルなどの一部の組み込み型はボックス化されており(つまり、内部的には可変構造へのポインターです)、make関数を使用して初期化されます。ポインターと非ポインターの間の構文を統一するためのアプローチでは、矢印(->)演算子が削除されました。ポインターのドット演算子は、逆参照されたオブジェクトのフィールドまたはメソッドを参照します。ただし、これは1レベルの間接参照でのみ機能します。

Java

Javaにはポインタの明示的な表現はありません代わりに、オブジェクト配列などのより複雑なデータ構造は、参照を使用して実装されますこの言語は、明示的なポインター操作演算子を提供していません。ただし、コードがnull参照(nullポインター)の逆参照を試みることは可能ですが、その結果、実行時例外がスローされます。参照されていないメモリオブジェクトが占めるスペースは、実行時にガベージコレクションによって自動的に回復されます。[16]

Modula-2

VARポインタは、プロシージャ呼び出しのパラメータと同様に、Pascalと非常によく実装されています。Modula-2はPascalよりもさらに強く型付けされており、型システムから逃れる方法が少なくなっています。Modula-2のバリアントの一部(Modula-3など)には、ガベージコレクションが含まれています。

オベロン

Modula-2と同様に、ポインターを使用できます。型システムを回避する方法はまだ少ないので、Oberonとそのバリアントは、Modula-2またはそのバリアントよりもポインターに関して安全です。Modula-3と同様に、ガベージコレクションは言語仕様の一部です。

パスカル

ポインターを備えた多くの言語とは異なり、標準ISO Pascalは、ポインターが匿名で動的に作成された変数を参照することのみを許可し、標準の静的変数またはローカル変数を参照することを許可しません。[17]ポインタ演算はありません。ポインタにもタイプが関連付けられている必要があり、あるタイプへのポインタは別のタイプへのポインタと互換性がありません(たとえば、charへのポインタは整数へのポインタと互換性がありません)。これは、他のポインター実装、特にPL / IまたはCに使用されるものに固有のタイプ・セキュリティーの問題を排除するのに役立ちます。また、ダングリングポインタによって引き起こされるいくつかのリスクを取り除きますただし、標準の手順( Cで見つかっdisposeたライブラリ関数と同じ効果があります)を使用して参照スペースを動的に解放できるということは、ポインターがぶら下がるリスクが完全に排除されていないことを意味します。[18]free

ただし、一部の商用およびオープンソースのPascal(または派生)コンパイラの実装(Free Pascal[19] Turbo Pascal EmbarcaderoDelphiのObjectPascalなど)では、ポインタは標準の静的変数またはローカル変数を参照でき、1つからキャストできます。別のポインタタイプ。さらに、ポインタ演算には制限がありません。ポインタを加算または減算すると、そのバイト数だけいずれかの方向に移動しますが、IncまたはDec標準のプロシージャを使用すると、ポインタが指す宣言されているデータ型のサイズだけポインタが移動します。型なしポインタも名前で提供されますPointer、他のポインタ型と互換性があります。

Perl

Perl プログラミング言語、ほとんど使用されませんが、packおよびunpack関数の形式でポインターをサポートします。これらは、コンパイルされたOSライブラリとの単純な相互作用のみを目的としています。他のすべての場合、Perlは参照を使用します。これは型指定されており、いかなる形式のポインター演算も許可しません。これらは、複雑なデータ構造を構築するために使用されます。[20]

も参照してください

参考文献

  1. ^ ドナルドクヌース(1974)。「ステートメントに移動する構造化プログラミング」 (PDF)コンピューティング調査6(5):261–301。CiteSeerX10.1.1.103.6084 _ 土井10.1145 /356635.356640S2CID207630080 _ 2009年8月24日にオリジナル (PDF)からアーカイブされました。
  2. ^ Reilly、Edwin D.(2003)。コンピュータサイエンスと情報技術のマイルストーングリーンウッド出版グループ。p。 204ISBN 97815735652192018年4月13日取得ハロルド・ローソンのポインター。
  3. ^ 「IEEEComputerSocietyアワードリスト」Awards.computer.org。2011年3月22日にオリジナルからアーカイブされました2018年4月13日取得
  4. ^ ISO / IEC 9899、条項6.7.5.1、段落1。
  5. ^ ISO / IEC 9899、条項6.7.8、段落10。
  6. ^ ISO / IEC 9899、7.17節、3項: NULL ...これは実装定義のnullポインタ定数に拡張されます。
  7. ^ ISO / IEC 9899、6.5.3.2節、段落4、脚注87:無効な値がポインタに割り当てられている場合、単項*演算子の動作は定義されていません...ポインタを参照解除するための無効な値の中には、単項*演算子はnullポインタです。..
  8. ^ a b Plauger、PJ ; ブロディ、ジム(1992)。ANSIおよびISO標準Cプログラマーリファレンスワシントン州レドモンド:MicrosoftPress。pp。108、51  _ ISBN 978-1-55615-359-4他のすべてのタイプはアレイに構成されたときに密集するため、アレイタイプには追加の穴は含まれません[51ページ]
  9. ^ WG14 N1124 C –承認された規格:ISO / IEC 9899 –プログラミング言語– C、2005-05-06。
  10. ^ ユング、ラルフ。「ポインタは複雑ですII、または:より良い言語仕様が必要です」
  11. ^ ユング、ラルフ。「ポインタが複雑です、または:バイト内に何がありますか?」
  12. ^ 米国特許6625718、Steiner、Robert C.(コロラド州ブルームフィールド)、「現在の場所に関連するポインタ」、2003年9月23日発行、Avaya Technology Corp.(ニュージャージー州バスキングリッジ)に譲渡 
  13. ^ 米国特許6115721、Nagy、Michael(フロリダ州タンパ)、「セルフポインターを使用したデータベースの保存と復元のシステムと方法」、2000年9月5日発行、IBM(ニューヨーク州アーモンク)に譲渡 
  14. ^ 「ベースのポインタ」Msdn.microsoft.com 2018年4月13日取得
  15. ^ Ulf Bilting、Jan Skansholm、「VägentilC」(Cへの道)、第3版、169ページ、 ISBN 91-44-01468-6 
  16. ^ Nick Parlante、 [1] Stanford Computer Science Education Library、pp。9–10(2000)。
  17. ^ ISO 7185 Pascal標準(非公式コピー)、セクション6.4.4ポインタタイプ以降。
  18. ^ J. Welsh、WJ Sneeringer、およびCAR Hoare、「Pascalのあいまいさと不安」、ソフトウェア:実践と経験7、pp。685–696(1977)
  19. ^ 無料のPascal言語リファレンスガイド、セクション3.4ポインタ
  20. ^ 連絡先の詳細。"//参照の作成(Perl参照とネストされたデータ構造)"Perldoc.perl.org 2018年4月13日取得

外部リンク