修正版
以前ポインタと参照に関する記事を書いていましたが、内容が雑だったため改めて書き直しました。NimではCライクなポインタと参照が用意されています。Cライクなポインタはアンセーフなもので、ガベージコレクタの管理外となっており、自身で管理しなければなりません。一方、参照はガベージコレクタの管理の対象となっているため、管理をガベージコレクタに任せることができます。
簡単なC言語のコードと比較してみます。
#include <stdio.h>
int main() {
int x = 5;
int* p = NULL;
p = &x;
printf("%d\n", *p); // output: 5
return 0;
}
このコードをNimのポインタを使って書くと以下のようになります。
var
x: int = 5
p: ptr int
echo repr(p) # nil
p = x.addr
echo repr(p) # ptr 0x56309c07cc28 --> 5
echo p[] # output: 5
p[] = 6
echo x # output: 6
ポインタ変数pは宣言された時点ではnil
となっています。そしてC言語でポインタが指すアドレスを取得する際は&
ですが、Nimではaddr
を使います。repr
を使うと、上記のようにアドレスを表示することができます。そして値を取得する場合は[]
を使います。上記のように
alloc
・alloc0
・realloc
などを用いていない場合、つまり動的にメモリー確保を行っていない場合は特に問題なさそうですが、もし使う場合はdealloc
で解放する必要があります。またヒープ領域のデータを保持しているものの場合、GC_unref
を使う必要があります。つまりCやC++同様、メモリー解放などの事後処理にいろいろと細心の注意が必要となります。一般的にメモリーエラーは以下のようなものがあります。
- 範囲外への書き込み・読み込み
- 共有メモリへの同時アクセス
- 未定義ポインタへのアクセス
- 解放後の使用(ダングリングポインタの参照外し)
- 不正なポインタの使用
- ヌルポインタの参照外し
- スタック・ヒープの枯渇
- 二重解放
- 無効な解放
- 不一致解放
一方、参照の場合は以下のようになります。
var
p: ref int
echo repr(p) # nil
p = new int
echo repr(p) # ref 0x7f051203b048 --> 0
p[] = 5
echo repr(p) # ref 0x7f051203b048 --> 5
echo p[] # output: 5
p[] = 6
echo p[] # output: 6
echo repr(p) # ref 0x7f051203b048 --> 6
上記の場合、int型への参照はnew
を使うことで0で初期化されるようです。こちらはガベージコレクタの対象なので、私でも安全に扱えるものだと思われます。
実はガベージコレクタも選べる
Nimはオプションとして、コンパイル時に--gc:〇〇
でガベージコレクタをいくつか選ぶことができます。参照カウント方式なら--gc:refc
、マークアンドスウィープ方式なら--gc:markAndSweep
、Boehm GCなら--gc:boehm
、Go言語のタイプなら--gc:go
、など。また--gc:none
でガベージコレクタなしにすることもできるようです。ちなみに、私にはハイレベル過ぎるお話なので、デフォルトのままにしています。これらの機能を見ていると、NimはCやC++などに精通した上級者向けの言語なのではないか?と今更ながら思うのでありますが、引き続きゆるゆると勉強をしていこうと思います。