(0.1 C言語の来歴)
初めにプログラムを実行する計算機の仕組みとC言語の様なプログラミング言語が必要とされる理由から話したいと思います。 ここで学ぶC言語は計算機のハードウエアに依存する部分まで細かく制御できる言語として作られているため、 他のプログラム言語に比べ計算機の仕組みが強く反映されているからです。 しかし、計算機の仕組みについては同じ期に開講される「情報生体システム工学基礎」できちんと説明されます。ここは一読する程度でかまいません。
ここで理解してほしいのは
計算機は記憶、演算、制御の3つを行う機械。(実際は、この他に外部との入出力が必要)
メモ: 上記のような計算機では、プログラム(計算手順)の読出し速度が演算速度と同じ程度には早いことが求められます。 例えばプログラムを穴の開いた厚紙(パンチカード)や紙テープから読み込んでいると、1秒間に1000個の制御命令を読み出すのも容易ではありません。一方で、現在の計算機の演算速度は1秒間に30億回程度です。
真空管を用いた最初の電子計算機であるENIACは1秒間に5000回の加減算ができました。パンチカードからプログラムを読んでいては、計算速度が遅くなってしまいます。そこでENIACでは演算装置を複数用意して目的の計算が行われるように演算回路の入出力を繋ぎました。計算する内容の変更は演算装置の設定変更とそれらの結線の変更で行われました。
ENIACは砲弾の弾道計算といった特定の目的の計算を行う専用計算機としては優れたものです。しかし、他の計算にも使おうとすると、プログラムの変更は設定スイッチの変更と結線の変更で行うため、大変手間のかかる作業でした。
そこで、ENIACの次の世代の計算機では 高速大容量の記憶装置を用意し、計算するデータだけでなくプログラムも記憶するようにしたプログラム内蔵型計算機が主流となりました。
プログラム内蔵型計算機:
記憶装置のデータを取り出して1個の汎用演算回路に送り、その演算結果を記憶装置に書き込む。ここで、演算対象のデータと演算の種類をプログラムで制御すれば様々な計算を行うことが可能となる。プログラム自体もデータと同じ記憶装置に記憶することで、プログラムの変更が容易になる。汎用性が高く低コスト。
ただし、計算速度に見合う速さでプログラムを読み出せる高速大容量の記憶装置(メモリー)が不可欠。
※プログラム内蔵型計算機はノイマン型計算機とも呼ばれます。プログラム内蔵型計算機の仕組みを広めた報告書がノイマンの名前で書かれていたことによる。
※プログラム内蔵型計算機は記憶装置のデータを1つづつ読み書きし、演算は1個の演算装置で1つづつ順番に実行するので、基本的に一度に一つのことしか実行しない。複数の演算装置を繋いで直接データを交換し並列に演算するデータフロー型計算機に比べて、プログラム内蔵型は計算が遅いのが欠点です。
今はほとんど見る機会が無いかもしれませんが、算盤(そろばん)と呼ばれる計算に使う道具があります。算盤は数値データを珠(たま)の位置で表し、容易に書き換えることのできる記憶装置といえます。筆算を行うときの紙と鉛筆と消しゴムに相当するものです。
下にある4つの珠を1つづつ上げて行くことで1+1+1+1=4のように加算もできます。算盤で工夫されているのは5を表す上の珠です。4+1=5となったときは、上の珠を下げて、下の珠4つを全て下げ5を表します。1つの桁で0〜9までの10種類のパターンが作れるので、十進法の1ケタ分のデータを算盤の1桁に記憶できます。
※このように1桁で0〜9までの加算や減算などもできるのですが、ここでは記憶装置と考えておきます。
計算機の場合はデータを0/1の様な2つの状態のどちらかで表すので、もし算盤を使って計算機の記憶装置(メモリー)を作るとしたら下のようになるでしょう。 この算盤では一番下から1,2,4,8,16,32,64,128を表す8個の珠で1桁になっています。1桁でなんと0〜255の256種類のパターンが作れます。
YES/NOや0/1など2つの状態の内どちらかを示す情報量を1bitと言います。
計算機の記憶装置では8bitを1つの纏りとして、1byteと呼ぶのが通例です。(8bit=1byte)
上のメモリーの図では、1つの珠の位置で1bit、 8個の珠が並んだ1桁で1byte の情報を記憶します。
記憶場所は算盤の桁に相当する番地(address)で区別します。この番地は上の図のように1byte毎に振るのが通例です。記憶装置の番地(アドレス)は膨大な数になり、4GB(ギガバイト)の 記憶装置を持つ計算機の場合で40億程度の数になります。
※ここでG(ギガ)は本来は109ですが、230の意味で使うことが在ります。210=1024でほぼ1000であることから230がほぼ10億になるからです。
※ギガ(G)は本来は109を意味し230ではありません。そこで230をギビ(Gi)と区別して呼ぶことが推奨されています。これに従えは4GBではなくて4GiBと表記することになります。Wikiを参照
上のメモリーを横に倒して、記憶装置の状態を簡単な絵にすると以下のように書けるでしょう。順番に並んだ番地ごとに 8個の0/1を記憶しています。(珠が上のときを1、下の時を0として8個の珠の状態を8個の01の並びで示しています)
番地 | 値 |
0 | 00000000 |
1 | 00000001 |
2 | 00000010 |
3 | 00000011 |
4 | 00000100 |
5 | 00000101 |
........... | .......... |
実際の記憶装置は、下記ICチップの模式図に示す様に、読み書きを指示する制御端子、番地を指示する複数のアドレス端子、データ を入出力する複数のデータ端子がついている。
アドレス端子
電圧ON/OFFの組み合わせで番地を示す
。2本の端子があればそのON/OFFの組み合わせは4通り、4本の端子があればそのON/OFFの組み合わせは4×4の16通りになる。8本で16×16の256通り。16本で256×256通り、32本では 232(=約40憶)通りと、アドレスを指定するための線は現実的な数でたりる。
データ端子
電圧ON/OFFの組み合わせで書き込みや読み出しのデータを示す。読み書きで同じ線を使う。
制御端子
電圧のON/OFFで装置の選択、選択されたときの書き込みや読み出しといった動作モードの指定を行う。
多くの人は10進数で四則演算(和差積商)を行います。足し算は始めは指を折りながら数えますが、経験をつむと1桁の足し算ぐらいは覚えてしまうので、後はこれを何度も組み合わせて桁の多い数の足し算もできるようになります。引き算も同様ですし、掛け算の計算は九九を記憶すれば足し算と組み合わせて計算できます。割り算もこの掛け算九九を使って計算しますね。私は知らないのですが、昔は算盤の仕組みに合わせた割り算の九九が使われていたそうです。
普段使う10進数は10個の数字(文字:0、1、2、3、4、5、6、7、8、9)と位取りによって数値を表わす方法です。2進数は使う文字が2個で同じように位取りを使う数値表現法です。 多くの場合、数字には文字0、1が使われます。
2進数(Two binary numbers)の四則演算のルールは10進数に比べてとても簡単です。だから計算機は0/1の様なbit単位のデータ表現を使い計算を行います。足し算は0+0=0, 0+1=1, 1+0=1, 1+1=10 の4種類を、掛け算は0*0=0, 0*1=0, 1*0=0, 1*1=1 の4種類を区別できれば原理的には十分です。
※実際には8桁、16桁、32桁、64桁の2進数の四則演算回路を作るため桁上げの仕組みも必要です。
※計算機の演算装置は四則演算だけでなく種々の論理演算の回路も用意しています。
※人間が計算機を使うためには、人間に解り易い10進数と計算機に好都合な2進数の変換が必要です。この変換は人間とのデータ入出力で必要になるだけなので、全体の計算時間に比べればごく一部になります。計算機の作り易さの方が優先されました。ENIACと呼ばれる最初の実用的な電子計算機は10進法で計算していましたが、以後の計算機は2進法を使って計算しています。
以下に、2進数の演算が機械でもできるほど簡単だということを体験してもらうために、例題を用意しました。
2の倍数は1,2,4,8,16,32,64,128、、、ですから100は次の表のように64の桁と32の桁と4の桁が1の2進数で表せます。
桁 | 64 | 32 | 16 | 08 | 04 | 02 | 01 |
値 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
100は64+32+4なので1100100です。
50は32+16+2なので110010です。 ※50は100の半分なので、01の並びが1桁下へずれます。
以下のJavascriptのフォームで10進数との対応を確認してみましょう
足して2になると桁上げが生じることを忘れなければ10進の足し算と同様です。
答えは10010110 10進数の128+16+4+2=150と一致します。
掛け算ですが、これも同じです。×110010なので1がある3か所だけ1100100をケタをずらして転記し、この3行を足し算すればOKです。九九のようなものは必要はありません。
答えは10010110 10進数の4096+512+256+128+8=5000で答えは10進の計算と一致しています。
桁 | 4096 | 2048 | 1024 | 0512 | 0256 | 0128 | 0064 | 0032 | 0016 | 0008 | 0004 | 0002 | 0001 |
値 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
※上の計算フォームは中段が3行しかないので掛ける数に1が3つ以上あると使えません。手抜きです^^。
ここで2進数は桁が多すぎて見難いので、計算機の世界では2進数4桁分をまとめて1桁で表せる16進数 (Hexadecimal number)で2進数を表記するのが通常です。数字には文字0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,Fの16文字を使います。
2進数 | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
16進数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
10進数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
メモリーの単位として使われるバイトは2進数8桁分なので、16進数の2桁できれいに表現できます。この為、1バイト分のデータを下記のように16進数2桁で表わすことが良く行われます。(10進数では0-255に対応)
00, 01,...0F, 10,...FE, FF
レジスタ(register)は演算装置の入出力値を設定する高速の記憶装置で、演算回路と一体化しています。レジスタは複数ありA,B,C..等の名前や役割名で区別されます。
計算機は、まず記憶装置から一旦レジスタに値をコピーします。次に、このレジスタの値を入力として演算を行い結果もレジスタに書きます。その後で必要ならレジスタの値を記憶装置に書き出します。できるだけデータをレジスタに置いて演算を進める方が、メモリーとデータを読み書きする時間が省けて高速な処理が可能です。
※現在の計算機はメモリーの読み書き時間が演算装置の演算時間に比べて1桁以上遅かったりする。レジスタは数が少ないので多くのデータを記憶できない。しかし、可能ならレジスタを使って計算をすすめ最後の結果だけをメモリーに書き込む方が処理が断然早い。
下の模式図の様に演算の種類と演算入力につながるレジスタの選択で多様な演算ができます。演算結果はAレジスタ (アキュムレータ)に書き込まれます。この時、同時にフラグレジスタに演算結果が、ゼロ、マイナス、桁溢れなどを示す1bitの情報を幾つかまとめたものが書き込まれます。各bitを旗が立っている か寝ているかに見立ててフラグ(flag)と言います。ゼロフラグが立っていれば、直前の演算結果がゼロであることを示します。
※この模式図ではAレジスタが演算の一方の入力に常に繋がっている。この様な設計にすることで制御回路の仕事の種類を少なくし、演算装置を単純化することができます。現在の演算装置がこうなっているということではありません。色々なものが在るのです。
※Wikiなどで色々調べてみよう^^
累算器(Accumulator)、レジスタ、歯車式計算機
- Wikipedia、Computer History Museum の
階差機関
(differrence engine)
電卓のボタンを
1、2、+、3、4、=
と押せば46と表示され12+34の足し算ができます。ここで、電卓のボタンを押す順番を書いたものをプログラムと呼ぶことにしましょう。プログラムを見て書かれた順番どうりにボタンを押す自動機械を作れば人手をかけずに計算できます。このようにプログラムを読んで演算と記憶を制御するのが制御装置です。
合計の計算を行う場合を考えてみましょう。記憶装置から数値を順番に読み出して足し算すれば合計が求めれれます。データが00001100番地と00001101番地のメモリーにあるのなら、
@記憶装置に00001100番地のデータの出力を指示し、演算装置に出力をAレジスタに記憶するように指示。
A演算装置のAレジスタの値をBレジスタにコピー
B記憶装置に00001101番地のデータの出力を指示し、演算装置に出力をAレジスタに記憶するように指示。
C演算装置にA+Bを計算してAに書き込みを指示。
D演算装置にAレジスタのデータの出力を指示し、記憶装置に出力を00001110番地に記憶するように指示。
E停止
...と連続して指示を出すことでメモリーの00001110番地に合計が求められます
このように、記憶と演算の回路に適切な順番で指示を出せば様々なことが可能になります。この一連の指示を出すのが制御回路の役目です。
※@Aの様な2度手間をせずに00001100番地のデータを直接Bレジスタにコピーすればと皆なさんは思うでしょう。しかし、この古い計算機では直接Bレジスタにもコピーできるような電子回路にはしなかったのです。これは、命令や回路の規模は小さく、計算は早くと様々な要求で取捨選択を行った、1つの結果です。全ての計算機がこうなっている訳ではありません。
プログラム内臓方式では、それぞれの制御に対応するビットパターン(命令コード)を決めておきます。この各制御に対応した命令コードを機械語(machine language)と呼びます。(機械語は計算機の機種で様々で す)
記憶装置のアドレス順に、制御手順どおりに機械語を並べたものがプログラムになります。
どのアドレスから命令コードを読むか記憶しておくプログラムカウンタと呼ばれる専用レジスタがあります。制御装置はプログラムカウンタで示されたメモリーから命令コードを読んで対応する制御を行い、同時にプログラムカウンタ値を読んだ分だけ増やす動作を繰り返します。この結果、制御装置は記憶装置のアドレス順 に命令コードを順番に実行することになり、プログラムが実行されます。
例えば記憶装置のX番地からAレジスタにデータをコピーする命令コードは00111010でXの番地は続く2バイトで示す。A+Bを計算しAに入れる命令コードは10000000。といった具合に機械語が決められているとしましょう。下記のように、命令コードとデータを記憶装置に格納し、プログラムカウンタを0番地に設定してから実行開始すれば合計の計算が行われます。下の表はメモリーの番地と書き込まれた値、その値の解説を並べたものです。
番地(上位_下位) | 値(2進) | 解説 | 命令コード/データ |
00000000_00000000 | 00111010 | メモリの値をAレジスタにコピー | 命令コード |
00000000_00000001 | 00001100 | データ1のメモリ番地下位1バイト | 命令コード |
00000000_00000010 | 00000000 | データ1のメモリ番地上位1バイト | 命令コード |
00000000_00000011 | 00100111 | Aレジスタの値をBレジスタにコピー | 命令コード |
00000000_00000100 | 00111010 | メモリの値をAレジスタにコピー | 命令コード |
00000000_00000101 | 00001101 | データ2のメモリ番地下位1バイト | 命令コード |
00000000_00000110 | 00000000 | データ2のメモリ番地上位1バイト | 命令コード |
00000000_00000111 | 10000000 | A+B=>A | 命令コード |
00000000_00001000 | 00110010 | Aレジスタの値をメモリにコピー | 命令コード |
00000000_00001001 | 00001110 | 書き込むメモリ番地の下位1バイト | 命令コード |
00000000_00001010 | 00000000 | 書き込むメモリ番地の上位1バイト | 命令コード |
00000000_00001011 | 01110110 | 実行停止 | 命令コード |
00000000_00001100 | 00010010 | データ1 | データ |
00000000_00001101 | 00110100 | データ2 | データ |
00000000_00001110 | 00000000 | 結果を書き込む場所 | データ |
※メモリーの値が命令コードなのかデータなのかは、値では区別できない。
制御装置は機械語を理解しているのではなく、命令コードで決められた制御を行っているだけです。その動作は機械語を理解して動いているように見えるかもしれません。
※楽譜を読んで演奏する自動オルガンと同じように、計算機はメモリー上に並んだ0/1のパターンを読んで動く装置です。
機械語は2進数の並びで人間にとってはプログラムの手間が大変煩わしいものです。1-1-4の例を以下に示すが、数値は2進数を01で書く手間を減らす為に全 て16進で表記しました。この例はintelの8080系の機械語で、メモリーの番地0000から1バイトづつ命令コードが書かれています。
※命令コードは機種で異なります。従って、同じ計算を行う場合でも異なる機種の計算機の場合には機械語を書き直す必要があります。
番地:値(16進表記)
0000: 3A ;000C 番地のメモリの値をAレジスタにコピー(3バイト使う命令コード)
0001: 0C ;..ここの2バイトが番地000Cを示す
0002: 00 ;..
0003: 47 ;Aレジスタの値をBレジスタにコピー
0004: 3A ;000D 番地のメモリの値をAレジスタにコピー(3バイト使う命令コード)
0005: 0D ;..ここの2バイトが番地000Dを示す
0006: 00 ;..
0007: 80 ;Aレジスタの値とBレジスタの値を加算してAレジスタに入れる
0008: 32 ;000E番地のメモリにAレジスタの値をコピー(3バイト使う命令コード)
0009: 0E ;..ここの2バイトが番地000Eを示す
000A: 00 ;..
000B: 76 ;命令を順番に実行するのを止める
000C: 12 ;データ1
000D: 34 ;データ2
000E: 00 ;データ3 0000番地からプログラムを実行すると値は46に変化する
機械語で直接プログラムを作るのは大変なので、機械語に1対1対応したアセンブリコード(ニーモニック)でプログラムを記述し変換表で機械語に翻訳する 方法が使われるようになりました。変換作業はプログラムで行うのが一般的で、このプログラムをアセンブラ(assembler)と言います。番地に名前を付けたものをラベル名と言い番地の代わりにラベル名でプログラムを記述できるなど、 機械語で直接プログラムを記述するよりも格段にプログラムが作りやすくなります。
アセンブリコードの例
アセンブリコード | 意味 | 機械語とデータ |
LDA 1234 | 1234番地からAレジスタにデータをコピー | 3A 34 12 |
STA 5678 | Aレジスタから5678番地にデータをコピー | 32 78 56 |
MOV B,A | AレジスタからBレジスタにデータをコピー | 47 |
ADD B | レジスタAとBの値を足してAに格納 | 80 |
HLT | プログラムの実行停止 | 76 |
DC 01 23 | この後に示す値をメモリーに設定する | 01 23 |
DS 2 | 2バイト分メモリを空けておく | ?? ?? |
アセンブラプログラム 番地:機械語(16進表記) PROG0 START 0000 ;プログラムPROG0の開始位置 0000番地 LDA XXXX 0000:3A 0C 00 ;XXXX番地の値をAレジスタにコピー MOV B,A 0003:47 ;Aレジスタの値をBレジスタにコピー LDA YYYY 0004:3A 0D 00 ;YYYY番地の値をAレジスタにコピー ADD B 0007:80 ;A+B=>A STA ZZZZ 0008:32 0E 00 ;Aレジスタの値をZZZZ番地にコピー HLT 000B:76 ;プログラムの実行停止 XXXX DC 12 000C:12 ;メモリーに16進で値12を格納 YYYY DC 34 000D:34 ;メモリーに16進で値34を格納 ZZZZ DS 1 000E:00 ;1バイト分メモリを空けておく END ;プログラムPROG0の終端 ※ 機械語の番地やラベルの値はアセンブラが計算してくれる
アセンブラプログラムはどのような機械語に変換されるかが明確な反面で、やはり長いプログラムを記述するにはまだまだ繁雑すぎます。
※上の例でアセンブラコードの1行に対応する機械語が複数バイトになるときは、1行に複数バイトを並べ、その代り次の行の番地をバイト数分増やしています。このような番地の計算はアセンブラのプログラムがやってくれるので、人間は番地を計算する作業から解放され、アセンブラでプログラムを作るときはラベル名XXXXやYYYYが何番地か人間は計算しなくて済みます。
より抽象的で人間がわかり易い人工言語を作り、翻訳プログラム「コンパイ ラ」で機械語に翻訳する手法です。 翻訳のしかたはコンパイラに任せ、人間は機械語やプログラムを実行する仕組みを詳しく知る必要がありません。
例えばC言語で、以下の様な合計の計算を含むプログラムを記述し、intelのx86系CPUの機械語に翻訳してみます。
#include <stdio.h> int main( void ) { char x,y,z; /* 16進数の数値を表すときは C言語では頭に0xを付けます 10進の255を16進では0xFFと書きます*/ x=0x12;/*16進数の12をxに代入*/ y=0x34;/*16進数の34をyに代入*/ z=x+y; /*x+yの値をzに代入*/ printf("%d",z); return 0; }
合計の計算手順はどのレジスタを使うか、いつメモリーに書き出すかなど無数にあるが、どうするかは翻訳するコンパイラ任せです。せいぜいメモリーを節約するか、実行速度を速くするといった要望が可能 な程度です。
次に翻訳結果を示しますが、x86系の機械語を知らないと全然解りません。 逆に言えばx86系の機械語を知らなくても機械語のプログラムが作れたわけで、計算機を使うためにプログラムを作る人の負担は大いに軽減されます。
番地:機械語 対応するアセンブラコード 00000000: 55 push ebp 00000001: 89 E5 mov ebp,esp 00000003: 83 EC 10 sub esp,16 00000006: C6 45 F4 12 mov byte ptr [ebp-12],18 ;ここがx=0x12(16進12は10進の18) 0000000A: C6 45 F8 34 mov byte ptr [ebp-8],52 ;ここがy=0x34 (16進34は10進の52) 0000000E: 8A 55 F4 mov dl,byte ptr [ebp-12] ;dlレジスタにxをコピー 00000011: 02 55 F8 add dl,byte ptr [ebp-8] ;dl+yを計算しdlに代入 00000014: 88 55 FC mov byte ptr [ebp-4],dl ;dlの値をzにコピー 00000017: 0F BE 45 FC movsx eax,byte ptr [ebp-4] ;この後はprintfの呼び出し処理 0000001B: 50 push eax 0000001C: 68 00 00 00 00 push offset _@2 00000021: E8 00 00 00 00 call _printf 00000026: 59 pop ecx 00000027: 59 pop ecx 00000028: B8 00 00 00 00 mov eax,0 ;この後はreturn 0の処理 0000002D: C9 leave 0000002E: C3 ret near
注意:上記のプログラムは毎回計算しなくてもzの値は決まっています。そこで、最適化を行うコンパイラではz=0x46と判断し、足し算しない 機械語プログラムを作
るかもしれません。もちろんx、yのためのメモリも用意しません。下記の様なプログラムに最適化してから機械語に変換するかもしれないのです。
コンパイラは早くて短い機械語プログラムになるように最適化の力を競っています。コンパイラを使うと計算機の仕組みが解らなくてもプログラムが作れます。でも自分のプログラムを逐語訳したものになるとは限りません。
#include <stdio.h> int main( void ) { printf("%d",0x46); return 0; }
注意:C言語のプログラム作りは間接的ですが機械語のプログラムを作ることです。アセンブリ言語で大規模なプログラムを書いていた人たちが、もっと解り易くプログラムを書くために作ったのがC言語です(教科書2P)。計算機の仕組みを少しは知っておくとC言語の文法の理解も容易になります。アドレスとかレジスタとかいう言葉が出てきたとき、ここを見直してください。
※計算機の仕組みを知らなくてもプログラムを作って計算ができるようにするためにFORTRANやCOBOLといった高級言語が作られました。実際、高級言語を使うことで多くの人が計算機を利用できるようになりました。しかし、C言語は高級アセンブラと呼ぶ人も居る程に、計算機の仕組みを知った上で使う言語です。組み込み機器の制御プログラム作りによく使われるのはそのためです。
計算機の機種ごとに機械語は色々です。アセンブラは機械語を記号化し、対応表で機械語に変換するだけです。同じ計算をしたくても、アセンブラのプログラムでは機種ごとに違うものを書かなければなりません。
一方で、高級言語のプログラム(例えばz=x+y;)の場合は、どの様な機械語に翻訳するかは言語処理系に任されています。そこで、計算機の機種による違いを言語処理系で吸収し、それぞれの機種に応じた機械語へ翻訳することができます。
※言語処理系
翻訳プログラムなど、そのプログラム言語を実行するために用意されたプログラムの実行環境。
同じ高級言語のプログラムを翻訳側で色々な機種の機械語へ翻訳する訳です。計算機の機種ごとに翻訳部分のプログラムを用意できれば、同じ高級言語のプロググラムが色々な計算機で同じように実行できます。
プログラミング言語利用者数ランキングの最上位はjavaとCやC++です。この科目では構造化言語であるC言語を学び、大規模なプログラムを作る上での基礎を作ります。Cはオブジェクト指向言語のjavaやC++を学ぶ上での基礎にもなります。同時に、Cは計算機の仕組みを理解して上手に使うことも可能にしてくれます。
※javaは計算機のハードウエアが違っても同じように動くことを重視した言語で、ハードウエアの機能を最大限に使うような処理は書けません。これに対して、Cは計算機のハードウエアを最大限に利用できる言語として作られたという特徴があります。このことがjavaとC/C++の棲み分けに繋がっています。
※1980年代にCにオブジェクト指向プログラムを書きやすくする機能を追加して作られたのがC++言語です。C++言語処理系の多くがC++だけでなくCのコンパイラも用意しています。このようなC++のCコンパイラの多くがC言語の規格C89(1989年)対応となっています。しかし、新しい規格C99(1999年)やC11(2011年)には部分的にしか対応していないのが現状です。
手続き型 プログラミング(Procedural programming)ではデータを処理する手続きを使ってデータを順番に加工して結果を得ます。機械語レベルであれば機械語の命令を順番に実行することで目的の結果を得ます。C言語の場合は式や関数の様なまとまった手続きを使うことで、プログラミングはより効率的になります。 しかし、問題を解くためにプログラムを作る側で具体的な解法手順を考え、それをプログラムとして記述することが必要なのは同じです。
C言語のプログラムを機械語へと翻訳するのはプログラムの1命令ごとに機械語に翻訳したものを順番に並べればいいので簡単です。しかし、
実際は最適化と呼ばれるより効率的な機械語に翻訳する機能が求められ言語処理系(コンパイラなど)は複雑化しています。
※言語処理系(language processor):プログラミング言語で書かれたプログラムを機械語にまとめて翻訳(compile)、あるいはそのまま逐次
解釈実行(interpret)する仕組み。
非手続き型の言語とは 問題を解くための具体的な演算手順をプログラムで記述するのではなく、前提条件や要求をプログラムとして記述 し言語処理系に要求を満たす手順作りを任せるタイプの言語です。
機械語は計算機の制御命令を記述するものなので手続的です。非手続型プログラムを処理するには要求を実現する 手順を作り出す必要があ り、無数に考えられる手順から最適に近いものを選択することも必要です。言語処理系の負担は大きいが、プログラムを書く側は具体的手順を考える必要が無い分だけ負担が減り、プログラミングが容易になると言われています。以下のような例が有る。
- LISP:
- Prolog: http://bach.scitec.kobe-u.ac.jp/prolog/
- SQL:リレーショナルデータベースの設計、操作(更新、検索)に使われる標準的な言語。