第30章 プログラミング(1)
第30章 プログラミング(1)
第1話 NP問題(1)
「オイラー路」を見つけました。
グラフ理論で、全ての辺を1度だけ通る路のことを、オイラー路というようです。
オイラー路には、次の2種類があるようです。
① オイラーグラフ:閉路を持つグラフ
② 準オイラーグラフ:閉路を持たないグラフ
①も②も一筆書き可能だそうです。
連結グラフGを考える時、
① Gがオイラーグラフ:Gの全ての頂点の次数が偶数
② Gが準オイラーグラフ:Gの頂点のうち、次数が奇数であるものがちょうど2つ
グラフGの頂点をすべて通る閉路はハミルトン路という。
連結グラフ:G上の任意の頂点間の辺(道)が存在するグラフ
頂点の次数:頂点に、接合する辺(道)の数
ハミルトン閉路問題に近付いたのかな?
第2話 コンピュータ・ソフト
かつて、SEを職としていた時の僕の設計からコーディングの方法を少し述べたいと
思います。
ここから先は、昔話になる可能性が大きいです。
現在は、Web中心のソフト開発が主流のように感じます。
しかしながら、変化したのは、ユーザ側から見た時であって、
開発者側から見れば、僕の世代と大きな違いは無いように感じます。
(本当かな?僕の主観かな?)
WINDOWSが主流になってから、困った事がいくつかありました。
① ハードを直接制御出来ない。
② アセンブラ(マシン語)でのコーディング規約が分からない。
①は、DDKを使用しました。
これは、あまり記憶に残っていません。
②は、WINDOWS規約を護って、中身だけをマシン語で書きました。
第3話 8086
僕が、若い時(30歳前後まで)のPCのCPUは、8086でした。
最後の方で、80386が登場したと記憶しています。
僕が、個人所有として、本格的なPCを手に入れた時のスペックは次の通りでした。
僕が、25歳前後の事です。
① N社製PC本体
② CPU:8086(クロック=16Mz)
③ Coprocessor:8087(数値演算プロセッサー)
④ Mainメモリ:640Kb
⑤ 増設メモリ:2Mb
⑥ 外付けハードディスク:20Mb
⑦ ドット・プリンター
②については、クロック以外に現在のCPUと違うところがありました。
記憶が定かでは、ありませんが。
1命令を実行する時間(サイクル数)が異なりました。
例えば、ADD(加算)命令が2~4サイクルで実行されるのに対し、
MULTI(乗算)は、2,3百倍のサイクル数が必要でした。
つまり、乗算の速度と加算の速度が違うという事です。
最大のサイクル数を必要とするのは、除算でした。
さらに、これらの演算は、自然数のみしか扱えません。
そこで、③が、必要になります。
このCoProは、8086と同期を取りながら、数値演算(実数)を行います。
さらに問題がありました。
8086は、16ビットCPUです。
0~65,535の数値しか扱えません。
負を考慮すると、その半分の範囲になります。
加算、減算は比較的簡単に32ビット数値の計算アルゴリズムを作れます。
問題は、乗算や除算です。
相当数のプログラム・ステップ数と処理速度に問題が出ます。
但し、それは標準ライブラリに用意されていたので、直接的な問題は、速度だけでした。
これをCoProで行えば、簡単なのですが、最後の桁の丸め(四捨五入など)の
影響で1だけ、異なる値を返す事があります。
ちょっとした細工が必要でした。
第4話 スタック
コンピュータ・ソフトでC言語があります。
C言語に限らず、プログラムは、Main()を先頭としてコーディングされます。
そして、コンパイルされて、マシン語の先頭アドレスがMain()となります。
Basicなどのようなインタープリタは、よく分かりません。
ある程度の大きさのプログラムになると、サブルーチンを持ちます。
サブルーチンと関数は、ほぼ同じです。
さて、どのようにしてこのサブルーチンを呼び出すのでしょうか?
そして、どのようにして、サブルーチンは動くのでしょうか?
コンパイルされた実行ファイル(EXE)は、PCのMainメモリにロードされます。
このロードされたEXEファイルの先頭アドレスは、OSが決めます。
実行ファイルの持つ様々なジャンプ先は、全て相対アドレスです。
ジャンプ先は、Goto文、Call文などの指定先です。
8086時代は、CSという便利?なものがありました。
これを使えば、必ずしも相対アドレスである必要はありません。
おそらくですが、EXEファイルは相対アドレスを想定して作られていたと思います。
何故なら、CPU依存にならず、汎用性があるからです。
本題に移ります。
8086は、スタックを持っています。
これは、SSとSPから構成されます。
SSはベース・アドレスだと思ってください。
実質的にこれが、悪さをする事はありません。
重要なのはSPとその仕組みです。
EXEファイルを作る時、スタック領域のサイズを指定します。
コンパイラのデフォルトを使って、意識した事がない方もいると思います。
メインメモリは、0番地~実装メモリのサイズまでアドレスを全てのメモリ位置に持っています。
現在のOSは、ユーザ不可侵の領域もあると思います。
この領域を意識して、あるいはプログラムの暴走で侵せば、例外が発生します。
例外は、何章か後に述べたいと思います。
ここでは、重大なエラーが発生するとだけ述べておきます。
指定したスタックのサイズを超えて、スタックを使用した場合、これも、
スタック・オーバーフローとなりエラーになります。
超えないように警告を発生させる事も可能ですが、自分がコーディングしたプログラム
意外のコードがコンパイラによって、自動的に埋め込まれます。
これは、かなりのオーバーヘッドとなります。
話が飛び飛びですいません。
メインメモリに、EXEファイルをロードした時、基本的に3つの領域が作られます。
① コード領域:プログラムのマシン語
② データ領域:基本的に静的メモリ、プログラムコードの外で宣言したメモリ
③ スタック領域:動的メモリ、サブルーチンの呼び出しに使用
その他、コード中でAlocate()関数などで追加メモリを作成した時、
これは、何処の領域に作られるのか?僕には分かりません。
スタック・オーバーフローした時、①や②、またはOS領域に食い込む可能性があります。
僕が開発のリーダーの時、サブルーチンの中に配列を宣言する事を、厳禁にしました。
この配列は、スタック領域に配置されます(動的メモリ)。
Main()もサブルーチンの1種です。
Callする時の仕組みは、次章に回します。
第5話 章の最後に
あれっ、棘がまた見つかりました。
それは、ソフトのコーディング時に侵してはならない事についてです。
頭の中が空っぽになるようで、とても楽になります。
システム開発時に、メンバー全てのレベルを同一にする事は不可能です。
同一にしようと努力した事もありました。
それは、失敗に終わりました。
プログラムが動いている仕組みを知っている人と、知らない人のコーディングで、
大きな違いがあります。
① 不要なエラー回避
② ソースの一貫性
③ 美しさ
①は、もちろんですが、②③もとても重要です。
まず、ソースのコード量が減ります。
そうすると、
① 単純ミスが減る。
② デバッグしやすい。
③ 作った人以外の人が見やすい
取り敢えず、今回はここまでという事でお願いします。
次章以降は、僕の課題と、僕の知る限りのプログラミング手法が主になりそうです。




