第31章 プログラミング(2)
第31章 プログラミング(2)
第1話 質
どうしても、頭から離れない事があります。
それは「この世界を構成する最小因子が質なのではないか?」
と、言う事です。
根拠は全くありません。
僕の「望み」なのかもしれません。
この地球に住む人々の多くが「力」を望みます。
「力」と「力」がぶつかれば、争いがおきます。
しかし「力」だけでは、この世界を構成する事が出来ないように、
思えて仕方がありません。
(例えば、宇宙の姿、地球の複雑性など)
(力=物質として考えています)
もう1つ、あります。
それは「同位置に異なる物質が同時間に存在する事は出来ない」
と、言う事は自明の理だと思います。
しかし、それを根拠にした科学情報や方程式に出会った事が無いような気がします。
僕は「座標や連続体の考え方は、この世界を近似させているだけなのではないか?」
と、思い始めています。
うまい表現の仕方が思い浮かびません。
何時か、叩き台を考えて見たいと思っています。
第2話 ハミルトン閉路問題
ハミルトン閉路問題について調べて見ました。
――――――――――――――――――――――――――――――――――――――――
以下 Wikipediaより
次のような性質を持つようです。
① |V(G)| ≥ 3、δ(G) ≥ |V(G)|/2 を満たす単純グラフ G は、ハミルトングラフ
② グラフ G (|V(G)| ≥ 3) がハミルトングラフ ⇔ d(u) + d(v) ≥ V(G) を満たす隣接
していない頂点 u, v について、G + (u, v) がハミルトングラフ
③ グラフ G (|V(G)| ≥ 3) がハミルトングラフで、(u, v) ∈ E(G) かつ
d(u) + d(v) ≥ n + 2 ならば、G - e もハミルトングラフ。
④ 完全グラフ K2n+1 は、n 個のハミルトン閉路に分解できる。
⑤ 完全グラフ K2n は、n-1 個のハミルトン閉路と 1 個の 1-正則な全域部分グラフに
分解できる。
――――――――――――――――――――――――――――――――――――――――
上記の全てを理解できたのでは、ありません。
これから学びます。
他にも面白い特徴があるようなので、それも合わせて学びます。
第3話 CALL
前章を読み返して見ました。
『話しに纏まりがない』
すいません。
纏めようとしても、難しいのです。
もし、読者の方が何かを求めるならば、拾い読みしてください。
ごめんなさい。
さて、コンピュータ・ソフトの記述言語にC言語があります。
C言語のCALLの仕組みを説明します。
その前に、スタックの捕捉です。
SPは、前章で説明しました。
しかし、スタックを扱う時、直接SPを書き換えません。
次の、2つの命令を使います。
(ワード:CPUに依存する数バイトの長さ)
① PUSH A:SPのアドレスにAを書きこむ。SPを1ワード分減らす。
② POP A:SPのアドレスからAに読み込む。SPを1ワード分増やす。
では、実際にマシン語レベルで行われている手続きです。
(1) CALL側の手続き
① CALLした現在のアドレスをPUSHする。
② PUSH ALL(全てのレジスタを確保しておく)
特に、BPは、大事です。
(サブルーチンは、ネストして行くので自分のSPを確保するため)
③ 引数を左から順にPUSHする。
(通常、アラインメントが行われワード単位にサイズが揃えられる。
つまり、引数が1バイトでもワードサイズとなって引き渡される)
④ CPがサブルーチン・アドレスに移る。
(2) サブルーチン側の手続き
① BPにSPをコピーする。
② サブルーチンで宣言されているローカル変数(動的変数)のサイズ分SPを
移動させる(増やす)。
記述中に、詳細まで適切か自信が無くなって来ました。
ですが、基本は適切なはずです。
Returnの時の手続きです。
(3) サブルーチン側の手続き
① SPを自分がCALLされる前の値に戻す。
(CALLした側では、CALL先のローカル変数のサイズを知る事が、
出来ません。実は知る事は、手続きをすれば可能なのですが)
② スタックからCALL元のアドレスを読み出し、Returnする。
③ 蛇足かな?関数の場合、特定のレジスタに戻り値を与えます。
(4) CALL側の手続き
① POP ALL(全てのレジスタを戻す)
ごめんなさい。
詳細は、突っ込まないでください。
要は、CALL元と、CALL先が整合のとれた、引数の受け渡しと、ポインタの
管理を行っていれば、コンパイラ・クローズになります。
誰も困りません。
ネストについて、簡単に記述します。
グラフのように、サブルーチンは、どんどん枝を伸ばして、深くなって行きます。
下位のサブルーチンをCALLする時、自分の状態を確保して、
おかなければなりません。
SPをBPにコピーする理由は、SPを常に変化可能にしておくためです。
さて、ここまでは僕の記述より専門書を読んだ方が確実だと思います。
僕の主張は、(2)の②です。
仮に、ローカル変数に配列を宣言した時、その配列を上回る(下回る)添数が指定されて、
そこに値が書き込まれると、スタックの中身が予期せぬ状態になります。
例えば、CALL元のアドレスが壊されると、Return出来なくなります。
(下回るは)添数が負になるケースがあります。
上回る(下回る)は、コーディングした人は「そんなはずは無い」と主張するかも
しれません。
多くの場合、これはバグによって引き起こされます。
「ローカル変数に配列を宣言しない」
これは、スタック・オーバーフローと上記が理由です。
もう1つあります。
これは、賛否両論あると思います。
ただ、僕は回避したい手法です。
それは「再帰」です。
再帰は、循環している間、常にスタックを増やし続けます。
永久ループを回避しようとすれば、そのためにローカル変数を増やします。
「さすが」と思った再帰関数がいくつかあります。
それは、Static変数を用いたものです。
これは、静的変数なので、スタックに積まれません。
しかし、循環している間、常にスタックを増やし続ける事に違いはありません。




