[初級] C++: プログラムのビルド（基本）

C++ のプログラムをビルドする際の超基本的な動き
プリプロセッサ, コンパイラ, リンカ

:::note info
本記事の前提条件は以下の通りです。
* 初心者向け
* とは言っても、何らかのプログラムはそれなりに書けるけど、C とか C++ はちょっと、という人向け
* ざっくり概要しか説明しないので細かいことは気にしないでいただきたい
* Visual Studio 2013 くらい～
* Windowsプログラム (CUI, GUI)
* コードの検証
    * 開発環境: Visual Studio 2022, x64, Release ビルド
    * 実行環境: Windows 10
:::

:::note info
* 本記事は上から順番に読む前提となっている
:::

:::note info
* 「C++」と書いてあるが、本記事の内容としては C でもほぼ同じ
* 「Visual Studio 2013 くらい～」と書いてあるが、本記事の内容だと現在過去未来のどのバージョンでもほぼ同じ
* 「Windowsプログラム (CUI, GUI)」と書いてあるが、本記事の内容だとどの OS でもほぼ同じ
* ただし、エラーコードなどは状況によって異なるので注意
:::

# ビルドとは何なのか、最小限の構成で確認
コンパイルに必要なファイルとは？
ソースファイル。以上。

```c++ main.cpp
void main()
{
}
```

実際これだけでコンパイルでき、exeも作成される。
生成された exe を実行しても何も起きないが、実行はできる。

:::note info
上記ソースコードだと警告が出るかもしれないので以降は下記のように戻り値を `int` にするのであしからず。

```c++ main.cpp
int main()
{
    return 0;
}
```

`main` 関数には普通もっと引数がある？まあそういうこともある。
:::

# 何が起きるか
`x64/Release` フォルダ（中間ディレクトリ）の下をみるとごちゃごちゃファイルができているが、ここで注目するのは `main.obj` ファイル。

1. コンパイラ `cl.exe` によって `main.cpp` がコンパイルされて、`main.obj` が作られる
1. `main.obj` がリンカ `link.exe` によってリンク処理されて実行可能なファイル形式である `hogehoge.exe` がつくられる

:::note info
リンカは指定されたオブジェクトファイルから `main` 関数を探し、それをエントリポイントとして実行ファイルを作成する。（ちなみにエントリポイントはオプションで変更できたりもする）
なので、下記は「コンパイル」は成功するが、「リンク」は失敗する

```c++ main.cpp
int xmain()
{
    return 0;
}

// LNK2001: 外部シンボル main は未解決です
// LNK1120: 1件の未解決の外部参照
```
:::

# 関数を作って呼び出す

```c++ main.cpp
void func()
{
}

int main()
{
    func();
    return 0;
}
```

ここでのポイントは、C++ のコンパイラはソースファイルを上から順に読むことしかしないので、必ず先になんらかの定義が必要だということ。
下記はコンパイルエラーとなる。

```c++ main.cpp
int main()
{
    func();  // C3861: 'func': 識別子が見つかりませんでした
    return 0;
}
void func()
{
}
```

# プロトタイプ宣言をする
コンパイラは、関数の実体は気にしない。呼び出しの仕様さえわかっていればコンパイルできる。このため、関数の呼出仕様をプロトタイプ宣言として前方宣言することで下記のように書ける。

```c++ main.cpp
void func();  // プロトタイプ宣言
int main()
{
    func();
    return 0;
}
void func()
{
}
```

:::note info
下記のようにするとどうなるか？つまり、プロトタイプ宣言のみで実体がコンパイルされていない場合。

```c++ main.cpp
void func();  // プロトタイプ宣言
int main()
{
    func();
    return 0;
}
```

これは、「コンパイル」は成功し、「リンク」に失敗する。
:::

# ヘッダファイルを使ってみる
「普通 #include \<hogehoge.h>とか書かない？」
使ってみる。

```c++ func.h
void func();
```
``` c++ main.cpp
#include "func.h"
int main()
{
    func();
    return 0;
}
void func()
{
}
```

# `#include` とは

コンパイラは、`#...` という命令文を見つけるとそれを「プリプロセッサディレクディブ」と認識し、コンパイルの前にプリプロセスを行う。
`#include` とは、「その位置に指定したファイルを読み込め」という命令である。
したがって、下記ソースファイルの場合、

``` c++ main.cpp
#include "func.h"
int main()
{
    func();
    return 0;
}
void func()
{
}
```

これをコンパイラに内蔵されたプリプロセッサが処理をすると、下記のようになる。

``` c++ main.cpp
// プリプロセス後

void func();
int main()
{
    func();
    return 0;
}
void func()
{
}
```

コンパイラはこれをコンパイルする。

:::note info
したがって、これでも問題ない。

```c++ func.h
void func()
{
}
int main()
{
    func();
    return 0;
}
```

```c++ main.cpp
#include "func.h"
```

:::

:::note info
プロジェクトのプロパティで、[C/C++]-[プリプロセッサ]-[ファイルの前処理]を「はい」にすると処理済みのファイルが中間フォルダに `*.i` として出力できる。（※その場合コンパイルはできなくなるのであしからず）
:::

# ソースファイルを分割する
「`func` 関数は別ソースファイルにしないとなんか変じゃない？」
コンパイラもリンカも特に何も気にしないが、人間にはわかりづらいので分けておくことにする。

```c++ func.h
void func();
```
```c++ func.cpp
void func()
{
}
```
``` c++ main.cpp
#include "func.h"
int main()
{
    func();
    return 0;
}
```

# 何が起きるか、再び
この3つのファイルからなるプロジェクトをビルドすると、何が起きるか。
Visual Studio でプロジェクトをビルドした場合の処理を追ってみる。

1. `main.cpp` が登録されているので、これをコンパイラに渡す
    1. つまり、`cl.exe main.cpp` を実行する
    1. コンパイラはまずプリプロセスを行う。 `#include` を見つけたので、`func.h` を所定の位置に読み込む
    1. コンパイルを実行し、`main.obj` を作成する
1. `func.cpp` が登録されているので、これをコンパイラに渡す
    1. つまり、`cl.exe func.cpp` を実行する
    1. コンパイラはまずプリプロセスを行うが、このファイルには特に処理するものはない
    1. コンパイルを実行し、`func.obj` を作成する
1. すべてのソースファイルがコンパイルできたので、リンカを実行する
    1. つまり、`link.exe func.obj main.obj /out:hogehoge.exe` を実行する
    1. `main.obj` が外部シンボル `func` 関数を必要としているので、他のオブジェクトファイルを探す
    1. `func` 関数が `func.obj` に見つかったのでこれをリンクする
    1. exe を作成するために必要なエントリポイントを探す
    1. `main.obj` に `main` 関数が見つかったのでこれをエントリポイントとしてexeを作成する