通常のプログラム(WindowsやLinuxなどで動くソフト・アプリケーション)を作りたい場合、CあるいはC++で大抵ことたりる。逆に言えば、これらさえ使えるならなんでも作ることができるといっても言いすぎではない。
CやC++などは通常高級言語と呼ばれる。そしてさらにこれらより解りやすく安全で作りやすい言語を目指し、JAVAやDなどが作られている。
しかし、いちど原点に立ち返って考えてみよう。Cを使おうがJAVAを使おうが、最後にはどんなプログラムもマシン語になるのだ。いや、もっと言えばコンピューターでなにか動作をさせるには、マシン語以外では絶対に動かないのだ。
(インタプリタ言語は別だが、あれとて内部ではマシン語になっている)
「OS自作入門」にも書いてあるが、逆に言えばマシン語さえ使えば(バイナリエディタやマシン語モニタ)どんなプログラムだって作ることができる。じゃ、なんでそれを使わないかというと、単純に「人間にはそれが煩雑過ぎて死にそうになるから」というだけだ。
(実はつい40年くらい前までは、この「死にそうになる」方法でプログラムが作られていたらしい。
たとえばAXレジスタに数値16をセットし、それに数値8を足し算したい。こんなのを考えると、人間は頭の中で、とにかく最初に 「+」を思い浮かべるだろう。しかし、コンピュータにこの動作をさせるためには
10111000,00010000 , 00000101,00001000 └────────┘ └───────┘ AXに16をセットしろ !AXの値に8を足せ!
と、こう信号を送る以外にはないのだ。それ以外では絶対にコンピュータは動作しない。(ペンティアムだろうがPS3だろうがMacだろうが!)
ちなみにこれらの数値(二進数)はどうやって決まっているのか。これはCPUを設計した時に「そうしよう」と決められただけ。なにが何番とかそういう分類や整理は一切ない。(つまり、もしこういう命令を覚えようと思ったら、単なる丸暗記以外には方法がない)
・・・昔はこんなことしてたんですねぇ・・・ほんとに信じられません・・・(^^;
で、これじゃぁあんまりなので、もう少し人間にわかりやすいようにできないものかと考えられ、二進数を16進数に直して書けばすこしはわかりやすいだろうということになりました。
B8,10,05,08
ほら! こんなに解りやすいでしょう!?
・・・・・・・orz・・・・・・・
前よりは、確かにナンボか読みやすくなりましたが、やっぱりそれでも「暗記」「暗号解読」になっちゃいますよねぇ。もうすこしなんとかならないかなぁ。
ちなみに、上記のヘンテコな暗号は、こういうふうに読みます。
B8,10, ← B8が「AXに値を入れろ!」という命令。後ろの10は10進数で16。 05,08 ← 05が「AXの値と足し算しろ!」という命令。後ろの08が足す数。
うーん。こういうふうに命令ごとに縦に割って書けばだいぶ解りやすくなってきましたね?。でも、やっぱり「足し算は05だ!」とか「代入はB8だ!」なんて、とても覚えていられませんよね?。こういう機械的な翻訳が得意な道具ってないかな・・・は! そうだ! あった!! コンピューターだ!!
ここで、アセンブラ言語が考え出されました。B8とか05とかじゃわからない(覚えきれない)けど、MoveとかAddとかJumpとかならなんとか覚えられるよね?。あと、数値も10進数でも16進数でも2進数でも自由に書けたら後でチェックする時にも楽じゃない?。そんなわけで、アセンブラ登場!。(しかし、上記に書いた通り、Moveとか言ってもコンピュータにはわからない。ちゃんと最後は2進数に変換してやらないといけないんですよね。これが変わったわけではないのです)
アセンブラ言語で書く マシン語できた〜! ┌──────┐ アセンブラプログラム ┌────┐ │MOV AX,16│ → が翻訳してくれる。 → │B5,10 │ │ADD AX,8 │ (これをコンパイルと言う) │05,08 │ └──────┘ └────┘
ちなみに、OS自作入門の中で
ADD CX,0x1234 は 81 C1 34 12 という4バイトの命令 ADD AX,0x1234 は 05 34 12 という3バイトの命令
という記載があります。お解りになりますか?。命令部分は「B8」とか「05」とか、1バイトとは限らないんです。「81,C1」という2バイトでひとつの命令になるものもあります。上記の図みたいに書き直すと・・・
アセンブラ言語で書く マシン語できた〜! ┌────────┐ アセンブラプログラム ┌──────┐ │ADD CX,0x1234│→ が翻訳してくれる。 → │81,C1,34,12 │ │ADD AX,0x1234│ (コンパイルと言う) │05,34,12 │ └────────┘ └──────┘
こういうふうに横に並べてみると、アセンブラは「言語」とは言っても、16進数のマシン語をちょこっとだけ見やすくしただけというふうに見えますよね?。数値を自動で計算してくれたり、ラベル機能が使えたりしますが。なんとな〜く想像してみてください。命令の一覧表が手元にあれば、アセンブラで書かれたソースを見ながら、手で(紙とエンピツ)マシン語に翻訳することは可能っぽくないですか??(ちなみに昔はまさにこれでやっていたそうです。呼び名はそのまま、「ハンドアセンブル」と言ったそうです)
さて。アセンブラの登場で、だいぶコンピュータのプログラミングは楽になってきましたが、やっぱりむずかしいですよね?。アセンブラは事実上マシン語です。CPUのレジスタやメモリなどの構造・理屈を知っていないと使えません。かと言って、人間が口で
- まず、数値をひとつ覚えろ。そうだな。じゃ、16にしよう。
- その覚えた数値に8を足し算しろ!
なんて言っても、コンピュータは理解できません。どうしよう・・・
そこでいよいよ、「高級言語」と言われる、より人間の感覚に近く、整合性が取れたプログラム言語が考え出されました。それがC言語です。(いきなりCが出来たわけではありません。それ以前にもいろいろ考え出されました。B言語とかフォートラムとか) C言語はアセンブラと違い、ずいぶんと人間の感覚に近いものになりました。(とは言っても、やはり口でしゃべれば解るなんて理想からすれば程遠いものですが・・・(^^; )
細かいことはおいておいて、C言語でプログラムを書くとこうなります。
Main{ int a; a = 16; a = a+8; }
・・・なるほど。少なくとも「なにをやっているか?」を推察するという意味では、アセンブラなんかに比べてもずいぶん解りやすくなりましたね?。
もうこれで、アセンブラなんか必要なくなりました。アセンブラなんて過去の遺物です。一度C言語が完成した以上、もう誰もアセンブラなんか必要ありません。アセンブラはこの世から消失していきました・・・・
大嘘です!!!!!
もう一度よく考えてみてください。コンピュータは、アセンブラ(マシン語)以外では絶対に動かないんですよ?。「でも現に、C言語で動いているじゃないか!」ですって?
違います。コンピュータはC言語で動いているのではなく、C言語が作り出したマシン語で動いているのです。
また現在でも、C言語は通常は直接マシン語を作ってはくれません。C言語で書かれた元のプログラム(ソースと言う)を一旦アセンブラに翻訳して(本当のアセンブラではないが、マシン語ではない)、そのアセンブラをさらに翻訳してマシン語(実行できる。つまりコンピュータが理解できるもの)を作り出しているのです。
これは解りやすい概念です。実際には細かい部分が違います
これはJAVAだろうがD言語だろうがC++だろうが同じです。
さて、話のC言語ですが、上記の例を見ればわかるとおり、解りやすく書かれたC言語はCコンパイラというソフトによってマシン語に翻訳されるのですが、実はC言語は、「入出力」の命令を一切持っていません。入出力。すなわち、
- 画面になにか表示する
- キーボードやマウス等からの信号を受け付ける
こういったことは、純粋な意味でのC言語では、一切できないのです。
「え〜?そんなアホな! だって俺は現にC言語でHello! Worldという文字を表示できているぞ!画面だぞ!ウソつくな!」
こんな声が聞こえてきそうですが、ウソではないのです。C言語の命令をいくら駆使しても、画面の文字を出すことはできません。
C言語は、自分自身で文字を出力することはできませんが、アセンブラやC言語等で作られたマシン語の命令の塊を関数として呼び出して使うことができるのです。
この、アセンブラやC言語で作られた様々な命令の塊を「ライブラリ」といい、ライブラリにどんな関数が入っているのかという目次を書いたものを「ヘッダーファイル」と言います。
ヘッダーファイル(関数の目次)をC言語のソースにはさみこむことで、それ以降はじめて、今までのCにはなかった命令が使えるようになります。
もっとも有名な「printf」関数。画面に文字を出す関数ですが、これもC言語に内臓されているのではなく、別に用意されたライブラリ+その目次であるヘッダーファイルを挟み込むことで初めて使うことができるのですね。
ソースファイル Cコンパイラ Main(){ ←ふむ。最初に実行される関数だね。 int a; ← 変数の定義か。これはできるぞ! a = 16; ←ふむ。変数に値を代入だね。OK。 printf("%f %e", a); ←・・・はぁ?なんだこの命令?しらんぞ! } ←がーん。へんな命令がある!翻訳できないよ〜!
純粋なC言語だと、こうなってエラーになってしまいます。
しかし、どこかの誰かがアセンブラやC言語で、printfという、画面に文字を出す命令を作ってくれました。そしてそれらの関数を何個かひとまとめにしてライブラリを作っておいてくれました。あとは、C言語の中にヘッダーファイルをはさみこみ、「こんな名前の関数を作っておいたよ。使っていいよ〜」とすれば、printfを使えるようになるのです。
ソースファイル Cコンパイラ <#include stdio.h> ←ふむ。stdio.hというファイルを読もう。なになに?いろんな関数があるなぁ。 Main(){ ←ふむ。最初に実行される関数だね。 int a; ← 変数の定義か。これはできるぞ! a = 16; ←ふむ。変数に値を代入だね。OK。 printf("%f %e", a); ←お?この命令は知らないけど、たしかライブラリにあるな。それをそのまま使おう! } ←よーし。全部知ってる命令だった。翻訳できた〜
とまあ、こういう仕組みになります。