LoginSignup
1
0

More than 3 years have passed since last update.

NimでnewSeqOfCapとsetLenを組み合わせて使うときの注意点

Posted at

はじめに

Nimはメモリ管理を意識せずにプログラミングできるが、バックエンドにCやC++があるため、
凝ったことをしようとする時にはNimがメモリ割り当てをどうやってるか意識しないといけない場合があります。

この記事の結論を先に述べると、
「良く分からない人はnewSeqOfCapsetLenを組み合わせて使わないようにしましょう。」

注意する例

問題

以下の(1),(2),(3)は何が出力されるでしょうか?

var
  seq1:seq[int]
  seq2:seq[int]
  seq3:seq[int]

seq1 = newSeq[int](10)

seq2 = newSeqUninitialized[int](10)

seq3 = newSeqOfCap[int](10)
seq3.setLen(10)


echo "seq1[0]:",seq1[0] # (1)
echo "seq2[0]:",seq2[0] # (2)
echo "seq3[0]:",seq3[0] # (3)

答え

(1) 0
(2) 不定
(3) 不定

解説

(1) newSeqseqを生成すると割り当てられるメモリ領域はseq要素の型(今回はint型)の初期値に初期化されるため、0となります。
(2) newSeqUninitializedは名前の通り初期化されないため、割り当てられたメモリ領域に入ってる値は分かりません。(その代わり、newSeqに比べて高速です)
(3) 今回注意したいのはnewSeqOfCapで、最初に確保したCapacity分のメモリ領域は初期化されません。setLenでseq長を伸ばした場合もメモリ再割り当てが起きない場合(最初のCapacityを超えない場合)は伸びた部分については初期化されません。

初期化されないことで問題が生じる例

先ほどのプログラムの前方にseqを何度も生成する処理を追加します。

var
  seq0:seq[int]

for i in 0..<10_000:
  seq0 = newSeq[int](100)
  for j in 0..<100:
    seq0[j] = 100

var
  seq1:seq[int]
  seq2:seq[int]
  seq3:seq[int]

seq1 = newSeq[int](10)

seq2 = newSeqUninitialized[int](10)

seq3 = newSeqOfCap[int](10)
seq3.setLen(10)

echo "seq1[0]:",seq1[0] # (1)
echo "seq2[0]:",seq2[0] # (2)
echo "seq3[0]:",seq3[0] # (3)
echo "seq3[1]:",seq3[1]

echo ""
seq3.setLen(1)
seq3.setLen(10)
echo "seq3[0]:",seq3[0]
echo "seq3[1]:",seq3[1]

この場合、環境によっては以下の結果となります。

seq1[0]:0
seq2[0]:100
seq3[0]:100
seq3[1]:100

seq3[0]:100
seq3[1]:0

seq0に割り当てられたメモリがGCで回収されてseq2seq3に割り当てられたため、100が出力されたと考えられます。

ただ、setLenでseq長を短くした場合は、削られた部分は初期化されるようです。
そのため、newSeqのみを使っている場合にはsetLenでseq長を変更しても安全と言えそうです。

まとめ

newSeqOfCapsetLenを組み合わせて使う場合には注意しましょう。

よく分からない場合はnewSeqを使うか、newSeqOfCapで生成したseqにsetLenは使わないようにしましょう。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0