Steep で不要な RBS::Location#to_s
の呼び出しを消すPRを作った。
RBSのメモリ使用量を減らす前準備。
RBSのメモリ使用量を減らすために色々やっているのだけど、計測用にベンチマークスクリプトを毎回書いているのは面倒なので、rbsのリポジトリに追加するPR。
Cを書いていて、謎のセグフォが出てそこそこ悩んだ。雰囲気は次のようなコード
#include <stdio.h> #include <stdlib.h> typedef struct { int id; int value; } entry; typedef struct { int len; int cap; entry entries[0]; } table; table *table_new(int cap) { table *t = malloc(sizeof(table) + sizeof(entry) * cap); t->len = 0; t->cap = cap; return t; } int main(void) { table *t1 = table_new(1000); for (int i = 0; i < t1->cap; i++) { t1->entries[i].id = i; t1->entries[i].value = i * 10; t1->len++; } table t2 = *t1; printf("t1 last value: %d\n", t1->entries[t1->len-1].value); printf("t2 last value: %d\n", t2.entries[t2.len-1].value); return 0; }
t1
もt2も同じstructなので同じ値が出力されると思いきや、私の手元だと
t2`を出力するところでセグフォが出てしまう。
t1 last value: 9990 zsh: segmentation fault ./a.out
この問題に最初遭遇したときは、t2
の方の書き方だけをしていたので、セグフォの原因がさっぱりだった。
その後t1
の書き方をすると解決することに気がついたのだけど、「えっ->
と(*v).
ってsyntax sugerの関係なんじゃないの??」としばらく理解ができていなかった。
これの答えが何だったかと言うと、構造体をコピーしていることが原因だった。
まず、entry entries[0]
の部分が影響している。これは可変長構造体、みたいな用語(正確な用語は知らない)のテクで、メモリ使用量を抑えるのに便利。
https://satosystems.hatenablog.com/entry/20100916/1284637817 とかを参考にしていた。
これは構造体の直後のメモリ領域に配列のためのメモリ領域を確保して、構造体に配列へのポインタをもたせることなく可変長の配列を実現している。便利。
ところが、table t2 = *t1;
で t1
を別のローカル変数であるt2
にコピーしている。ということは、このt2
が保存されているアドレスはもともとmalloc
してきたアドレスとは異なるということ。t2.entries
はt2
の直後の領域を参照しようとして、セグフォになってしまっていた。