「組み込み」ならではの基礎知識 ――スタートアップ・ルーチンからハードウェアまで
1 プログラムはどのように動くのか
プログラムがどういったしくみで動いているかということ(プログラムのランタイム構造)は,一般のC言語の解説書には書かれていません.それは,WindowsやLinux/ UNIXといったOS上で動くプログラムを開発する場合,ランタイム構造はOS側で決まっており,C言語を使ってプログラミングする人はそれを気にしなくてもいいからです.
でも,組み込みソフトウェアの開発ではそうはいきません.それは,使用できるメモリ量に制限がある(つまりメモリ量が少ない)のと,デバッグ環境が必ずしもWindowsやLinux/UNIXのときのように整っているとは限らないからです.
実際には,こんなことを面と向かって説明してもらえる機会は少なく,ちょっと勉強すればもう少しサクサクとしごとができて楽だろうにという人が多いのが現実のようです(それに,プログラムがどう動いているかわからないなんて,くやしいではありませんか).
ここでは,プログラムのランタイム構造の基本的なメカニズムについて説明します.
●例:再帰呼び出しのプログラム
プログラムのランタイム構造を説明するために,まずは自然数nの階乗計算を考えます.自然数nの階乗n!は,
n! = n × (n-1) × ... × 2 × 1 (1-1)
と計算します.
また式(1-1)において,(n-1) × ... × 2 ×1とは(n-1)の階乗のことですから,
n! = n × (n-1) ! (1-2)
とも書けます.
さて,nの階乗を計算する関数factをコーディングしてみます.ここでは式(1-2)を使ってコーディングします.
int fact(int n) { if (n == 1) return 1; return(fact(n-1) * n); }
関数factの中で自分自身を呼び出していますね.これを再帰呼び出しと言います注1-1.
例えば,fact(3)の計算(3!)は次のように行われます.
fact(3):3==1 ではないのでfact(2)*3を返す そのためにfact(2)を計算する fact(2):2==1 ではないのでfact(1)*2を返す そのためにfact(1)を計算する fact(1): 1==1 なので1を返す
fact(2):fact(1)の値が1だったので, fact(1)*2 = 1*2 = 2を返す fact(3):fact(2)の値が2だったので, fact(2)*3 = 2*3 = 6を返す
つまり,fact(3)を計算するためには,fact(2)とfact(1)の計算が必要になります.計算過程を図1-1に示します.
まるで,fact(1),fact(2),fact(3)という関数factのコピーが三つあるようですね.さて,この関数factが実際にどのように実現されているか,わかりますか?
注1-1;再帰呼び出しを使うとプログラムをエレガントに書けるが,組み込みソフトウェアでは通常は使わない(関数factの例なら,式(1-1)をwhile文を使って書く).ただし,本記事ではあえて再帰呼び出しを例にしている.