外部参考:
誰もがつまずくポインタを完璧理解
C 言語にポインタがある理由は省メモリ化・高速化・開発作業の省力化です
ポインタのメリットと必要性
ポインタとは何ですか?
ポインターは、記憶域へのアドレスを保存する、4バイトのデータ型(32ビットのシステム)または8バイトのデータ型(64ビットのシステム)です。
ポインタはデータを収納していません。ポインタは、初期化されると、データを指し示します。
初期化していないポインタは、何も指し示さなくて、未定義です。
ポインタを理解するのに、玉子のパック、それぞれの「穴」(卵を置くところ)の底に、No.1〜12 を印刷した鶏卵ケースを考えてください。
これらの穴はコンピュータの記憶域に似ています。
各穴、または記憶域には、アドレスがあります。この例では、1〜12 です。
卵が、データの項目を表すなら、穴1の卵は、1のアドレスを持っていることになります。
通常は、変数を使って、データに直接アクセスします。
ある型の変数を Dim(DIMension) するとき、データのために、保存スペースを設定しています。
このとき、データがどこにあるかを、知ったり気にかけたりする必要はありません。変数を通して、直接データにアクセスできるからです。
これは、穴の下部に書かれた数を見ずに、穴 1 で、卵を見つけて、卵をつかむことと似ています(データを読む)。
あるいは、穴1に卵を入れることに似ています(データを保存)。
ポインタを使うのは、少し異なっています。
ポインタを表す紙の切れ端があると想像してください。
今、それは、空白で、何も示しません。
ポインタが初期化されるまで、この未定義のポインタは、使えません。
ポインタを初期化するには、ポインタに、1 を書きます。
今、ポインタは、鶏卵ケースの穴 1 を「指しています」。
データ(卵)を、穴 1 に入れるために、紙の切れ端を見て、穴 1 にそれを合わせて、卵を、この穴に置きます。
卵を検索するために、私たちはまさしく正反対をします。
メモ用紙を、穴 1 に合わせて、次に、卵をつかみます。
卵を、置くこと、得ることの全ては、メモ用紙を通して行われなければなりません。これは、ポインタに「反-参照をつけ」ると呼ばれます。
つまり、ポインター (No.1) に含まれている参照アドレスを介して、データを取得します。
ポインターは、データを含んでいません。
ポインターは、データへの参照アドレスを含んでいます。
FreeBasic では、
Dim と
Ptr 命令文を使ってポインタを定義します:
この命令文は、上の例で、私たちの空白の紙に一致しています。
ポインタは、何も示さないで、未定義です。
今、ここで、ポインタを使おうとすると、プログラムはダウンするでしょう。
ポインタを使えるように、ポインタを初期化しなければなりません:
Dim aptr
As Integer Ptr
aptr =
Allocate
(SizeOf(Integer))
ここで、
Allocate を使って、
Integer のために、メモリに、別の十分なスペースを設定します。そして、そのスペースのアドレスを、aptr に登録します。
sizeof マクロは、渡されたデータ型の、バイトで表現されるサイズを、返します。
Sizeof の代わりに len を使うこともできます。(.13b 以降)
いったんポインタを初期化すると、ポインタを使えるようになります:
*
aptr =
5
Print "aptr:
";
*aptr
'演算子* を使うと、ポインターが指すメモリー位置に格納されている実際の値にアクセスできます。
aptr の前の
* 接頭語に注意してください。
* は、間接参照演算子です。
これは、メモ用紙上の数を、鶏卵ケースの穴の数に、突き合わせることに、似ています。
* 演算子 を使うことで、aptr が指し示す穴に含まれるデータ(卵)に達することができます。
下は、完全なプログラム例です:
'
Option Explicit
Dim aptr
As Integer Ptr
aptr =
Allocate
(SizeOf(Integer))
*aptr =
5
Print "aptr:
";
*aptr
Deallocate aptr
Sleep
deallocate 関数は、aptr によって指し示されたメモリを解放します。そして、aptr はもう一度未定義になります。
これは、私たちのメモ用紙の上で、数を消しているようです。
もし、aptr を、割り当て解除した後に使うと、プログラムはダウンします。
ポインタを使うと何がよいのか?
FreeBasic にポインタを加える主な理由は、多くの外部ライブラリが、型構成へのポインタと、文字列へのポインタを必要とすることです。
例えば、
Win32 API には、ポインタを通して、書き込んで、そして関数に渡さなければならない、多くの構造があります。
ポインタの別の使い方は、
型 の定義です。
FreeBasic の
型 定義は、固定長文字列を含むことができるだけです。しかし、プログラムが動くまで文字列の長さを知らないと、どうなるでしょうか?
ポインタは、この目的に役立ちます。
(型定義は、現在、可変長文字列をサポートできます。)
'
Option Explicit
Type mytptr
'ポインタを使うにはデータ型を宣言する必要があります。
sptr
As ZString Ptr 'Pointer か Ptr を使ってポインタ変数を宣言します。
End Type
'この関数は、渡された文字列のために、スペースを割り当てます
'そして、それを記憶域にロードします。そして、
'文字列へのポインタを返します
Declare Function pSetString
(ByVal s
As String) As ZString Ptr
'型の変数
Dim mytype
As mytptr
'型定義に、変数文字列を設定します
mytype.sptr = pSetString
("Hello World From FreeBasic!")
Print "aptr:
";
*mytype.sptr
Deallocate(mytype.sptr
)
Sleep
End
Function pSetString
(ByVal s
As String) As ZString Ptr
Dim sz
As ZString Ptr
'chr(0) のために、スペース + 1 を割り当てます
sz =
Allocate
(Len(s
) +
1)
'文字列を記憶域に登録します
*sz = s
'ポインタを戻します
Return sz
End Function
ここでは、項目 sptr で、型を、
zstring ptr と定義します。
Zstrings は、ヌル終わる文字列で、多くの外部のライブラリによって使われます。そして、動的割当てのために設計されています。
型を定義した後、
dim 命令文で型のインスタンスを作成します:
Dim mytype As mytptr
そして、
型 定義で欲しい、可変長文字列のアドレスを得るために、関数 pSetString を呼びます。
mytype.sptr = pSetString
("Hello World From FreeBasic!")
sptr はポインタとして定義されていて、文字列変数ではないことを、覚えておいて下さい。このため、pSetStringは、文字列自体ではなく、文字列へのポインタ(メモリ・アドレス)を返します。
言い換えれば、文字列が、穴 #1 にあるなら、pSetString は、1 を返します。
関数 pSetString は、一時的な
zstring sz を、使って、渡された文字列パラメータ s のために、スペースを
割り当て ます。
zstring は、ヌルで終わる文字列なので、関数を
割り当てる ときに、ヌル終端文字列(null terminator)のために、s の長さに 1 を加えなければなりません。
'chr(0) のために、スペース + 1 を割り当てます
sz = Allocate
(Len(s) + 1)
文字列にいったんスペースを割り当てると、間接参照演算子 * を使って、データを記憶域にロードします。
'文字列を記憶域に読み込みます
*sz = s
そして、ポインタ(文字列のアドレス)を、型に戻します。型は、mytype.sptr に、保存されます。
'ポインタを返します
Return sz
今、間接参照演算子を使って、型の中の文字列を、間接参照できます。
Print "aptr:
";
*mytype.sptr
ポインタは、初心者には分かりにくいかもしれません。ポインターがデータを含んでいないことを心にとめておけば、混乱することはありません。ポインターは、何かデータを指すだけです。
ポインタはメモリアドレスです。そして、間接参照演算子 * を通してそのデータを操ります。ポインタは、普通の変数と、あまり変わりはありません。
sancho3 が、2018年2月7日に修正しました。