見出し画像

C言語 - ローカル変数の恐怖(スタックの話)

少し面倒な話なので、C言語教室とは独立して上級者向けの話をします。部分的にC++の話も出てきますが、根は同じ話です。

その昔、gcc ではなく msc を使っていたときに、alloca という新しいライブラリ関数を見つけました。

alloca関数

今でこそ gcc でも使えるようですが、見つけた時は「これは便利だ」と乱用した覚えがあります。

ALLOCA

これを使えば解放忘れを気にすること無く、しかもパフォーマンスも良い!いい事ずくめに思えます。もちろん確保したメモリを呼び出し元に返す使い方はできませんが、作業用として確保する目的であれば充分です。

ところが実行するとプロセスが落ちるんですね。そうです「スタック領域不足」です。このトラブルは主に再帰呼び出しを行うと起きる問題なのですが、メモリ確保でもやらかすことになってしまいました。

C言語のローカル変数は基本的にはスタック領域に割り当てられるのですが、普段はどのくらいスタックが使えるかは無頓着です。今でこそそれなりに潤沢な領域が割り当てられるのですが、昔の msc な時代は64Kに制限されることが多かったです。このあたりを調べるにはどうすれば良いでしょうか。これは言語の範囲を超えてコンパイル(というよりリンク)の設定であったりOSの機能として設定されることが多いです。

gccで各関数が使用しているスタックサイズを出力するオプション

c++で配列のサイズが大きく多くて実行時にエラーが起こる場合の対応

うっかり!?複雑なオブジェクトを使うことが多いC++ではCの時よりも配慮が必要になることが多いです。もちろん組み込みの場合などはもっとシビアで、スタックが溢れた場合は対象プログラムだけではなくシステム全体を巻き込んで落ちてしまうこともあります。

mallocの場合は領域を確保できなかった場合に NULL が返ることにより何かしらの処理(やりようのないこともシバシバですが)を行うことができますが、alloca はそもそも確保できなかった時の明確な規定(NULL を返すことが書いてある実装もありますが)がなく、いきなり signal を発して(ここで拾える可能性は無くはないものの)落ちるのが普通です。

そもそも言語上のスコープとスタックの関係は、実装の上で理解はできますが、仕様としてどうなのかは、わりと曖昧です。C言語の関数内でのスコープではスタックフレームを更新しないのが普通です。このため関数内であればスコープを抜けても、値は生きています(有効ではないし、次にスコープ内に変数が割り当てられた場合は重複して使われる可能性がある)。また関数を「呼び出した」場合、呼び出し元のローカル変数はまだスタック上にはあるのですが、これを使っていいかどうかは微妙なところがあります。

またC言語においては setjmp/longjmp を使った場合や、C++での try/catch 構文では、スタックが破壊されるので、 alloca で確保したメモリがどこかに行ってしまうこともあります。

alloca

さらに悪いことにはスタックの問題はスレッドを使うことにより、よりシビアになります。スレッドを起こすと、スレッドごとにスタック領域が割り当てられるのですが、これはプロセスの指定とは別の指定です。一般的にはプロセスで使えるよりも小さな値が割り当てられ(昔の話ですが、当時はデフォルトで2Kとかでした)、ここで alloca なんて使おうものなら、あっと言う間にスタックが溢れます。多めの領域が必要であれば適切な手順を踏んで拡張しておかなければなりません。これが環境依存の上に結構ややこしいんですね。

余談ですが……

Linuxのマルチスレッドプログラミングにおけるスタックサイズの設定について

スタックはあまり丁寧に管理されているわけではないので、オーバーランしても値が使えてしまったり、セキュリティホールになることがあります。最近の実装ではスタックへの変数の割り当てが推測されないように工夫されるようになりました。

C++ - スタックベースのバッファーを保護するための Visual C++ サポート

※Linuxにおいても類似の仕組みが導入されています。

スタックに変数を割り当てるのは何もC言語だけの話ではないのですが、言語仕様としては存在しない領域であっても自由にアクセス出来てしまうのがC言語なので問題が顕在化するということでしょうか。いずれにせよスタックはヒープほど無いので、ローカル変数のサイズは程々にというのが教訓のようです。

ヘッダ画像は以下のものを使わせて頂きました。

https://www.irasutoya.com/2015/08/blog-post_36.html



この記事が気に入ったらサポートをしてみませんか?