なが月・8日目

8日目

帰宅後から開始。

C言語、今日は構造体をポインタで参照する場合について学んだ。構造体struct someStruct fooのメンバにアクセスするにはfoo.someMemberであるが、構造体のポインタstruct someStruct* fooを通してアクセスする場合はfoo->someMemberだということで、Perlの影がちらつく(もちろんCが先でPerlが倣ったのであろうが)。そしてlinked list(連結リスト)と呼ばれるデータ構造の登場である。連続した固定長のメモリ領域を確保する配列とは異なり、リストの要素はメモリ上で連続しているとは限らない。各リスト要素は「次の要素が存在する場所」のポインタを保持し、挿入に際して挿入要素には前の要素に格納されたポインタを格納、前の要素には挿入要素へのポインタを格納することで多少パフォーマンスを犠牲にしつつも柔軟に要素数を変更できるということだった。

さらにstdlib.hのmalloc/freeが登場してきた。メモリリークという言葉を思い出しつつ進めると、mallocはvoid*という型で返すので、これを望みのポインタ型にキャストして代入することで確保したメモリ領域を参照することができることが分かった。ここでsizeof()が重要となってくるが、sizeof(int) = 4であり、4byte = 32bitであるから最初の1bitを正負に割り当てると必然的に-(2^31) ~ (2^31) - 1までしか表現できないということが実感できた。また調べたところtypeofはGCC拡張であり、Clangでは利用できないという。コンパイラと最適化レベルによって生成されるバイナリと実行結果が異なることがあり、またコンパイラによって対応している関数さえ異なる(独自拡張なので当然だけれども)というのは厄介に感じる。

ポインタの扱いに慣れてきて、変数のコピーを感覚的にとらえられるようになった気がする。ポインタaをコピーしてそこから参照するのはオリジナルを参照するのと同じで、これは鏡像のように感じられた。実体に傷がつけば鏡像にも傷がつく。そしてプログラムの世界では逆もまた真なりである。

一方ポインタaが示す実体&aをコピーしたものは印刷のようで、その時の&aの状態を示してはいるが、一方に何を書き加えても他方に変化はない。

Python

a = [1, 2, 3]
b = a

は鏡像コピーで、一方を変えると他方も変化する。

a = [0]*3  # [0, 0, 0]

の各要素はプリントコピーだが、

a = [[0]*3]*2  # [[0, 0, 0], [0, 0, 0]]

のa[0]とa[1]は鏡像コピーである。メモリ節約のためだろうか?

さすがにa[0][0]とa[0][1]が鏡像コピーでは迷惑極まりないが、幸いこれらはプリントコピーである。

リスト内包表記

a = [[0 for _ in range(3)] for _ in range(2)]

はすべてプリントコピーである。

今日は連結リスト周りであれこれ調べていたら結構な時間を費やしてしまった。リスト内包表記を讃えよう。