（執筆中）『低レイヤを知りたい人のためのCコンパイラ作成入門』を学習するときに試行錯誤したこと

### テキスト

[低レイヤを知りたい人のための Cコンパイラ作成入門](https://www.sigbus.info/compilerbook/)

### WSL Ubuntuの起動ができない

Power Shellの管理者モードで何かを実行する。エラーメッセージを見て検索すれば、わかるはずです。

### Ubuntuマシンをバックアップ・リストアしたい

[WSLのバックアップを検討する \- concerti\.tsukubaのブログ](http://concerti.hatenablog.com/entry/2018/02/06/214659)

- tarコマンドの最後のスラッシュを忘れないこと。
- Google Drive File Streamには保存できない（Ubuntuから見えない）

以下のようなシェルスクリプトを作っておくと、いつでもバックアップできる：

```sh
date=`date "+%Y%m%d"`
sudo tar -cvpjf "/mnt/c/bkup/WSL/$date/backup.tar.bz2" -X /mnt/c/bkup/WSL/exclude_list.txt /
```

### objdumpできない

```sh
$ objdump -d -M intel /bin/ls

Command 'objdump' not found, but can be installed with:

sudo apt install binutils
```

言われたとおり`$ sudo apt install binutils`する。

### Visual Studio CodeをUbuntuにインストールする

#### まず、WindowsにX Serverをインストールする

「X410」というアプリがWindows Storeにあります。まったく設定が要らないのがウリ。よくセールしているようです。

#### 次にVSCをインストールする

[Visual Studio Code for Linux を WSL 上の Ubuntu 16\.04 LTS にインストールする \- 451 Unavailable For Legal Reasons](https://tech.xeres.jp/2018/05/30/installing-vscode-for-linux-in-wsl-ubuntu-16.04-lts/)

VSCの起動以外に日本語化とかいろいろ書いてある。とりあえず以下のことをするとWindowsから日本語文字列をコピペできるようになる。

```sh
$ sudo apt install -y language-pack-ja
$ sudo apt install -y fonts-noto-cjk
```

日本語入力はめんどくさそうなので私はパス。

export LANG="ja_JP.UTF-8"

以下は`.bashrc`に追記しておく：

```sh
export DISPLAY=":0.0"
```

ちなみに``Control　+　` ``でコマンドプロンプトを表示できる（右下）。

![test.sh - 9cc - Visual Studio Code 2018-11-22 23.27.02.png](https://qiita-image-store.s3.amazonaws.com/0/12939/9e799e50-cb2d-c509-9ebb-35149106644e.png)

コマンドプロンプトは最初はもたつくが、「レンダリング方法を変えますか？」とか聞かれるのでYesと答えるとスムーズに動くようになる。

- `*.c`ファイルを保存すると勝手に「C/C++エクステンションをインストールしますか？」と聞いてくるので入れる。普通のエクステンションとインテリセンスがある。
- `*.s`ファイルを保存するといろいろエクステンションを提案してくるので、`x86 and x86-64 Assembly`を入れる。
- 日本語の入力は面倒くさそう。`vi`ならWindowsの入力システムが使えるので、簡単に入力できる。

#### フォントサイズを変更する

デフォルトのエディタのフォントサイズは小さいので、大きくする：

[VS Code の便利なショートカットキー \- Qiita](https://qiita.com/12345/items/64f4372fbca041e949d0#%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88%E3%82%B5%E3%82%A4%E3%82%BA%E7%B3%BB)

### 引数を処理するコードが理解できない

賢い方々に教えていただきました（ありがとうございました）。

[Cのポインタとアセンブリの対応関係、およびポインタを使うメリット \- Qiita](https://qiita.com/weed/items/a0eaa1f74f7948d69c24)

それでも`strtol`関数がよくわからないので考えてみました。

現時点のコンパイラに食わせる引数が`12+34`だとします。コンパイラが以下のようにメモリに格納したとします。

|-57番地|-58番地|-59番地|-60番地|-61番地|-62番地|
|---|---|---|---|---|---|
|1|2|+|3|4|\0|

```c
char *p = argv[1];
```

コンパイラが`*P`を-12番地に格納したとします：

|-12番地|
|---|
|-57番地|

最初の1字は以下のように処理するのでした：

```c
printf("  mov rax, %ld\n", strtol(p, &p, 10));
```

`strtol`関数が出てきました。

**書式**

```c
long strtol(const char *s, char **endp, int base);
```

**パラメータの型と説明**

|パラメータ	|説明|
|---|---|
|const char *s	|変換する文字列|
|char **endp	|変換終了位置|
|int base	|基数|

少し悩んだのですが`strtol`は**1字だけ**ではなく**2字以上の文字列**でも処理できるんですね。しかも`&p`、ここでは**-12番地（*p）**、も更新してくれる。高機能！

>atol関数は、単に文字列を10進のlong型変数に変換するのみで、文字列中に変換不可能な文字があった ときには対応できませんが、strtolを使うと、変換可能な部分についてはきちんと変換し、文字列中に変換不可能な文字があった場合には、その文字列のポインタをendptrに格納します。

[strtol - 初心者のためのポイント学習Ｃ言語](http://www9.plala.or.jp/sgwr-t/lib/strtol.html)

もう一度、本のコードを見てみましょう：

```c
printf("  mov rax, %ld\n", strtol(p, &p, 10));
```

|-12番地|
|---|
|-57番地|

|-57番地|-58番地|-59番地|-60番地|-61番地|-62番地|
|---|---|---|---|---|---|
|1|2|+|3|4|\0|

1. `p`（変換する文字列の先頭のアドレス）は**-57番地(p)**の`1`です
2. 数字なので`p`を1つ進めて**-58番地(p)**にセットして次へ進みます
2. 次の番地（-58番地）は`2`です
3. 数字なので`p`を1つ進めて**-59番地(p)**にセットして次へ進みます
3. ここまでで文字列`12`を読み込みました
4. 次の番地（-59番地）は`+`です
5. 数字ではないので読み込みを止めます
2. このとき`&p`（変換終了位置）は-59番地です
3. 関数は**int**型の`12`を返します

なお、基数は`10`なので、数字は10進数で解釈します

ここで以下のコードを実行すると、**char**型だった`1`と`2`が**int**型の`12`になります。そして、以下のようなアセンブリを出力します：

```
mov rax, 12
```

わざわざ**int**型に変換したのには深い理由があるのでしょう。

|-12番地|
|---|
|-59番地|

これによって`p`は-59番地の`+`になります。

|-57番地|-58番地|-59番地|-60番地|-61番地|-62番地|
|---|---|---|---|---|---|
|1|2|+|3|4|\0|

それから、以下のようなループに入ります：

```c
    while(*p) {
      if (*p == '+') {  // 第1引数の途中で「+」が出てきたら、
        p++;  // pを1増やして、
        // 該当するアセンブリを出力します
        printf("  add rax, %ld\n", strtol(p, &p, 10));
        continue;
      }

      ...  // 繰り返す処理

    }
```

具体的には以下のようなプロセスになります：

```c
        p++;
```

**-12番地（*p）**の「-59番地」を1増やします：

|-12番地|
|---|
|-60番地|

これによって`p`は-60番地の`3`になります。

|-57番地|-58番地|-59番地|-60番地|-61番地|-62番地|
|---|---|---|---|---|---|
|1|2|+|3|4|\0|

次に…

```c
        printf("  add rax, %ld\n", strtol(p, &p, 10));
```

これによって以下のようなアセンブリが出力されます：

```
add rax, 34
```

そして最終的に`*p`は`\0`を指し、ループは終了します。

まとめると、以下のようなアセンブリが出力されました：

```
mov rax, 12
add rax, 34
```

うーん、難しかった…

### gccできない

```sh
$ sudo apt install build-essential
```

### git pushできない

まず、リモートリポジトリを削除する。これには以下のようにする（たぶんこれをしないとssh鍵生成などのめんどくさいことをする羽目になる）：

```sh
$ git remote remove origin
```

あとは以下のページの通り：

[ローカルでgitを使用した後、gitHubに登録する方法 \- Qiita](https://qiita.com/koshihikari/items/dcf126fa9c0de2b6fa7e)

#### VSCでgit管理

ちなみに一度gitの設定をしておくと、あとはVSC上からadd、commit、pushなどのgit操作ができる（他のコマンドもひと通りできる。branchを切ったりmergeしたりもできる。詳しくは下記リンクを参照）。

[Visual Studio Code の git 連携機能と git コマンドについて \(2018/05/23\) \- Qiita](https://qiita.com/satokaz/items/4660ce57ca8eb456a096)

さらに`Git History`というエクステンションを入れておくと、コミットログを見ることができる。

[Git History \- Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=donjayamanne.githistory)

View -> Command Palette で「git history」と入力すれば良い。

![Git History - 9cc - Visual Studio Code 2018-11-22 23.15.28.png](https://qiita-image-store.s3.amazonaws.com/0/12939/34e17eeb-e6ea-ab15-14ad-54690db9a8ad.png)

ファイルのdiffも見ることができる。

![test.sh (6ac0e18 ↔ 86099fd) - 9cc - Visual Studio Code 2018-11-22 23.17.05.png](https://qiita-image-store.s3.amazonaws.com/0/12939/7686a778-31d3-ea25-0486-e25c87a103f0.png)

### 抽象構文木のコードがわからない

まず、以下の部分は間違っています：

```c
  Node *node = malloc(sizeof(Node))
```

最後に`;`を付けましょう。

#### malloc、ヒープメモリ

さて、この`malloc`という関数、何をしているのでしょう？

>ヒープメモリから size バイトのブロックを割り当てます

[malloc - 初心者のためのポイント学習Ｃ言語](http://www9.plala.or.jp/sgwr-t/lib/strtol.html)

ヒープメモリ？

>例えば、ファイルを読み出す際、ファイルのサイズは毎回同じとは限らないので、ファイルサイズ分だけメモリをヒープに確保すれば、無駄にメモリを消費することはないよね。

>これをスタックで実現しようとすれば、ファイルサイズが最も大きくなる場合を想定しておかなければならない。

>スタックは少なくすればスタックオーバーフローの危険性があるし、大きくすれば無駄に使われないメモリが存在することになるよね。スタックサイズの調節は、限られたメモリを最大限有効に使うための、プログラマーの腕の見せ所だ。

[ヒープとスタック \| 学校では教えてくれないこと \| \[技術コラム集\]組込みの門 \| ユークエスト株式会社](https://www.uquest.co.jp/embedded/learning/lecture16.html)

なるほど、入力するデータ（プログラムコード）は小さいことも極端に大きいこともあるので、極端に大きいデータ（極端に長いコード）だとスタックメモリがあふれてしまうことがある。ということですね。

ちなみに、`malloc`の戻り値は以下のようになっています

成功時　：　確保したメモリブロックを指すポインタ
失敗時　：　NULL　（メモリ不足により指定サイズ分のメモリが確保できないとき）

#### それでもmalloc、ヒープがわからない

賢い方にとても良く理解できる方法を教えてもらいました。

[mallocでメモリを確保して操作する様子を、ヒープを直に見ることで、観察する（Visual Studio 2017を使って） \- Qiita](https://qiita.com/weed/items/c036843372fcb4609597)

（次はフィボナッチ数列の予定）