新人技術者のためのロジカル・シンキング入門(2) ―― プログラミングにおける良いデータ構造
【2】外部変数に対する解決策(2/2)
● 使い捨てデータはローカル変数とダイナミックの二つ
次に,使い捨てのデータ領域について考えていきましょう.使い捨てのデータ,通常,ローカル変数と呼ばれる変数で実装されます.ほとんどのシステムにおいて,使い捨てデータはローカル変数のみ考えておけばよいと思われています.しかし,組み込みシステムのようにメモリ・リソースが極めて限られている場合は,そうもいかないケースが多々あります.以下に,使い捨てデータ領域についての注意点をまとめます.
1) スタック・オーバフローに注意
ローカル変数はリンカによってスタックと呼ばれる領域にとられます.メモリ・リソースが限られた環境の場合,ローカル変数をむやみにとると,スタック・オーバフローが起きることがあります(図4).
OSがあれば警告を発してアプリケーションを強制終了させられる.しかし,組み込みシステムではOSにそのような機能が備わっていないものもある.その場合,すぐには気づかないため,デバッグがたいへんになる.
例えば,関数の中に領域長の大きな配列がいくつも使われていた場合,このスタック・オーバフローが起こりえます.ややこしいことに,このスタック・オーバフローは,モジュールの単体テストでは出現しないのに,モジュールを呼び出し元のシステムに結合すると初めて出てくることがあります.スタックは関数の呼び出しが積み重なるたびに積み上げられていきます.そのため,疑似メイン部のすぐ下にモジュールが位置しているので,単体テストでスタック・オーバフローを起こすことはめったにありません.しかし,結合されると関数階層の深いところに位置するようになるため,スタックを使い切ってしまうということが起こりえるのです.
2) OSに頼れない開発環境もある
読者のみなさんの中には,「スタック・オーバフローが起これば,OSが警告を出してアプリケーションが強制終了されるのではないか」と思う方がいるかもしれません.しかし,組み込みソフトウェアの開発環境では,そのような機能を備えたOSを用いているとはかぎりません.そのような場合,目に見える現象としては,単なるシステムのハングアップとしか映らないからやっかいです.スタック・オーバフローがハングアップの原因だと気づくまでには,かなりの時間がかかることでしょう.
そのため,ここではローカル変数とは別に,要素数の大きな配列などは「ダイナミック・データ」と呼ばれる領域に集めてしまい,モジュールの中の関数ごとに共用するようにします(図5).ローカル変数は,配列でない変数に限定されることになります.こうすると,スタック・オーバフローが防げるからです.使い捨てのデータ領域にもローカル変数とダイナミック・データの2種類が存在することになります.なお,スタックの消費量は,ローカル変数の総量でのみ決まります.
各関数に大きな配列が散らばっていると,全体でスタック・オーバフローを起こしかねない.対策として,各関数のローカル配列はダイナミック・データ領域に集めてしまう.これらの領域は,処理に用いる寿命が重ならなければ使い回しできるので,領域の節約になる.
このように大きな使い捨て領域を集中管理することによって,スタック・オーバフローを防ぐことができます.それだけでなく,関数ごとに領域を使用する「寿命」が重ならないと判明した場合には,同じ領域を使い回すようにすることができるので,メモリ・リソースが限られた組み込みプログラムを作る際には適した設計方針といえます.