C99だと可変長配列(Variable-length array, VLA)というものが使えるということをいまさらながら知ったので、調べてみた。
可変長配列
C言語で配列を宣言する際に従来は配列の要素数は定数である必要があり、実行時にサイズが決定する場合には malloc
で領域を確保する必要があった。
別の選択肢として、malloc
の代わりに alloca
を使用すると関数を抜ける時点で自動的に解放されるので便利
(ただしANSI Cではない)。
void foo(int len) { |
これがC99では可変長配列が使えるとのこと:
void foo(int len) { |
用例
「ああ、これは alloca
みたいなもんでしょ」と安易に思っていたところ、cppreference.comにいろいろ難しい例が載っていた:
sizeof
でサイズを取得できるgoto
で戻って繰り返せる- 関数のプロトタイプ宣言ではサイズに
*
を指定できる- 関数の実体定義時には値に式を指定する
- スコープ内の型宣言に使える
typedef int VLA[m][m];
動作・実装の確認
void use_in_loop(int n) { |
forループのスコープ内で使用した場合にはバッファが同じアドレスになるのはまあわかるとしても、gotoでも同じ動作になるとは「え、どうなってるの?スコープを抜けるわけじゃないのになんで戻るの?」という感じだった。
objdumpでコンパイル結果を見てみた:
$ clang -O2 -c vla.c && objdump -S vla.o |
どちらもコンパイル結果は大体同じで、
- VLAでの操作前のスタックポインタを退避(
%r12
)しておき、戻る前に復帰させる - VLAのsizeofは実行時にどこかの領域に格納しているんではなく、確保時の式と同じ値を使用する
alloca
のように関数から抜けるときに一括でスタックポインタが巻き戻されるんじゃなく、ちゃんとスコープやラベルに応じて増減される。
雑多
C99の後、C11で必須の仕様ではなくオプション扱いになったとのこと。
サポートされてない場合には __STDC_NO_VLA__
が定義されている。
またC++でもサポートされてない(VLAとは違う可変長配列の仕様となっているらしい)。
参考
- Array declaration - cppreference.com
- Why does C++ not support a variable length array and restrict it as supported in C? - Quora
- Variable-length array - Wikipedia
- Linus TorvaldsはVLAの使用を削除したとのこと: “USING VLA’S IS ACTIVELY STUPID! It generates much more code, and much slower code (and more fragile code), than just using a fixed key size would have done.”
- て別にこの使用例がおかしかっただけだった。固定長で済むんだったらそれに越したことはない。
- The Linux Kernel Is Now VLA-Free: A Win For Security, Less Overhead & Better For Clang - Phoronix
- Using the GNU Compiler Collection (GCC): Variable Length
- ジャンプで飛び入るのは禁止とのこと、ぐぬぬ…。
- gcc拡張で、構造体のメンバにも使えるとのこと…。