技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

プログラム言語の源流をざっくり

目標

プログラムってどう動いてるの?なんでプログラム言語なんてものが必要なの?を理解する。
それが分からないとプログラミング言語のありがたさなんてわからないんで…

初めに 0 と 1 がある

とは皆聞いたことがあるはずだ。プログラムは全部 0 と 1 で動いているんだと。
でも誰も実感なんてできないはずだ。

そこで、死ぬほど単純な 1+1 の結果を表示するプログラムをコンパイルして、その中身を 01 表示する。
長いので一部抜粋するけどこんな感じになる。

010011010101101010010000000000000000001100000000000000000000000000000100000000000000000000000000111111111111111100000000000000001011100000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111110000000000000000000000000000000111000011111101110100000111000000000101101000000100111001101001000011011100000000001010011001100110100100001010101000110100001101001011100110010000001110000011100100110111101100111011100100110000101101101001000000110001101100001011011100110111001101111011101000010000001100010011001010010000001110010011101010110111000100000011010010110111000100000010001000100111101010011001000000110110101101111011001000110010100101110000011010000110100001010001001000000000000000000000000000000000000000000000000000000000001000000010011000011111000000010000001000010110101010000010100010000010000101101010100000101000100000100001011010101000001010001000011010101010111000011010100010000111000101101010100000101000111111100010111010101010101010000000101110010110101010000010100011111110001011101010101000101000000001000001011010101000001010001111111000101110101010011010100000000010100101101010100000101000111111100010111010101000101010000000000010010110101010000010100010101111101000101010100010101000000000110001011010101000001010001000001000010110101010001010100010010101100101101010100000101000110110011010111000101100101010000000001010010110101010000010100011011001101011100101011110101000100000101001011010101000001010001101100110101110001010010010100000000010100101101010100000101000101010010011010010110001101101000000001000010110101010000010100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001010000010001010000000000000000010011000000000100000101000000000010101011011111011001000101111100000000000000000000000000000000000000000000000000000000000000001110000000000000000000100000000100001011000000010000111000011011000000000000111000000000000000000000000000010100000000000000000000000000000000000000000000000000011010010001001000000000000000000000000000010000000000000000000000000000001000000000000000000000000000000000000001000000000000000000000000010000000000000000000000000000000000100000000000000000000001100000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000001000000000000000000000000000000000000000000000000000000001100000000010000001000000100000000000000000001000000000000000000000001000000000000000000000000000000000000000100000000000000000000000100000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011110000100101000000000000000010110100000000000000000000000000000000000100000000000000000000001110000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101000000000000000000000101000000000001000000000000000000000000001000010000000000000000011100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111000000100001000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000110010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

うん、読めたもんじゃねぇ。
これでもルールはある(ってかないとコンピュータだって読めない)のだけど、ホント無理。

因みに、初期のコンピュータはこの 0/1 を紙に穴を開けて読み込ませてたらしい。
その頃のコンピュータは表現も何も数字と、かろうじてアルファベットしか返せなかったから、それでもやってやれなくはなかったそうだ…

この 0/1 に直訳するのがアセンブラ

これは VisualStudio2019 Community 等でコンパイルする際に、プロジェクト構成をいじるとアセンブラコードも出力される。
これも長くなるので、ある程度抜粋。

  00028  c7 45 f8 01 00
    00 00        mov     DWORD PTR _i$[ebp], 1
  0002f c7 45 ec 01 00
    00 00        mov     DWORD PTR _n$[ebp], 1
  00036 8b 45 f8     mov     eax, DWORD PTR _i$[ebp]
  00039 03 45 ec     add     eax, DWORD PTR _n$[ebp]
  0003c 89 45 e0     mov     DWORD PTR _r$[ebp], eax

コードの意味だけど

  00028  c7 45 f8 01 00
    00 00        mov     DWORD PTR _i$[ebp], 1
エントリポイントからの相対位置(0x00028)、 コマンド内容の16進数翻訳(c7 45 f8 01 00)
         2バイト空白(00 00)  転送コマンド(mov)   32ビット(DWORD )  転送先ポインタ(PTR _i$[ebp]), 値(1)

「値 1 をポインタ _i$[ebp] に 32 ビットデータとして転送する」
とまぁ辞書片手に意味は読めなくはない…*1

  • エントリポイント: アプリケーションファイルの中の実際に実行を開始する場所のこと。
  • ポインタ: パソコンのメモリアドレスを指し示すもの。(厳密な話は省くが)CPU はアホだから、一度に 32 の 01 羅列しか覚えられない。そんなもんだから、覚えておくためのスペースが必要だ。それがメモリ。(たまにSSDとかをメモリという人もいるが、厳密にはあいつらはストレージという)

感覚的な説明をするなら、

  • CPU=単純な計算しかできないアホの子(ただしメチャクソ早い)
  • メモリ=命令書やノートを広げる作業台(CPU がそこそこ早く取り出したり書き込んだりできる)
  • ストレージ=実行前の命令書や仕事結果を置いとく本棚(探したり、書き込んだりは遅いけど、本棚だけあって量は多く持てる)

で、残りもざっくり訳すと

    mov   eax, DWORD PTR _i$[ebp] ; eax レジスタに、32ビットの PTR _i$[ebp] アドレスの内容を格納
    add  eax, DWORD PTR _n$[ebp] ; eax レジスタにて、32ビットの PTR _n$[ebp] アドレスの内容を加算
    mov  DWORD PTR _r$[ebp], eax ; 32ビットの PTR _r$[ebp] アドレス に eax の内容を格納

因みにレジスタとは、CPU の中にある超小さい(かつ、メモリより圧倒的に早い)データ領域のこと。
アドレスを PTR _i$[ebp] こんな感じで表現してる(物理的メモリアドレスを直接記載していない)のは、プログラムを起動したときに、メモリのどこに配置されるかは不明なので、先頭メモリからの相対位置にラベルを付けて管理してるから。

分かったところで書きたくはないな(苦笑

しかも、このアセンブラというやつは 01 命令の直訳だ。
とはいえ意味のある形式なので、まじめに書こうとしたら、「どのレジスタに何を入れて」とか頑張って書けばやってやれないことはない。
でも正直こんなの書いてらんないだろう?

だからもう少しマシな書き方をして、それをアセンブラに翻訳(コンパイル)する。

そしてC言語でこれを書く

もうアセンブラ(A言語)はこりごりです。
って言って、 B言語 - Wikipedia なんてのも生まれはしたのだけど、データ型の概念などがなく、あくまでアセンブラよりマシレベルだったという…。
そこで皆聞いた事のある C言語が現れる。

(つっても以下のコードは C++だけど…)

#include <iostream>

using namespace std;

int main() {
    int i = 1, n = 1;
    int r = i + n;
    cout << r;
    return 0;
}

因みに上記のアセンブラは以下の部分

 int i = 1, n = 1;
    int r = i + n;

どれだけスッキリしたことか…

でももっと自然に書けないの?そう思った人は居るかも知れないが、さっきも書いたように最終的にアセンブラにしなければならない。
逆に言うと「整数、数字とかの表記ゆれができない」、「命令や値の区切りを明確にしないと置き換えできない」みたいな制約がつく。 だって人間が翻訳するわけじゃないから、文章の雰囲気や行間を読むなんてことはできない。

数学的にきっちりした作りでないと自動的な翻訳はできないわけだ。
なので、プログラム言語を覚えるときは「言語」ではなくて「式」だと思って書いた方が覚えが早いかもしれない。

最も最近は AI が割と綺麗に日本語を英語やフランス語に翻訳し始めてるので、近くはなくともそう遠くない未来で自然な言葉でプログラムできるようになるんじゃないかな…