gcc コンパイルオプション備忘録

個人的な備忘録です。
間違いがありましたらコメントいただけるとありがたいです。

## 参考サイト
[gccコンパイラの使い方](http://kaworu.jpn.org/c/gcc%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9)
[江添亮のC++入門](https://ezoeryou.github.io/cpp-intro/)
[リンカの役割 自分メモメモ](https://qiita.com/saikoro-steak/items/e5089a2b33b52d5410bd)
[[C言語] 共有ライブラリと動的ライブラリを整理する](https://qiita.com/edo_m18/items/b9765ff3313d5a13f82f)

## 実行環境
* Ubuntu 16.04 LTS
* gcc 5.4

<hr>

### a.outを作る
```shell-session
$ gcc main.c
```
`gcc` が `main.c` を処理し、 `a.out` という実行ファイルが作られる。

### 実行ファイルに名前をつける
```shell-session
$ gcc main.c -o main
```
実行ファイルに名前をつけるときは `-o` オプションを使う。
実行後、 `main` という名前の実行ファイルが作られる。

### 複数のファイルから実行ファイルを作る
```shell-session
$ gcc main.c hello.c -o main
```
`main.c` `hello.c` という2つのファイルから `main` という実行ファイルを作る。

※ 例

```c:main.c
#include <stdio.h>

void f();

int main() {
  f();
}
```

```c:hello.c
#include <stdio.h>

void f() {
  printf("hello\n");
}
```

上記のコマンドでこの2つのファイルをコンパイルして、 `./main` を実行すると

```shell-session
$ hello
```

と出力される。

### オブジェクトファイルを作る
オブジェクトファイルはコンパイルはしたがリンクが終わっていない状態のファイルのこと。
gccでは以下のステップを踏んで実行ファイルが作られる。

* プリプロセッサ
    * コンパイルを行う前にソースコードに対して行われる前処理
    * c/c++では, ディレクティブ(`# ...`)という命令を処理する。

```c:ディレクティブの例
#include
#define
#if
#ifndef
#elif
#endif

などなど...
```

* コンパイル
    * ソースコードをチェックして、中間言語や機械語に翻訳する。

* リンク
    * オブジェクトファイルやライブラリを連結して、実行ファイルを作る。

例えば、 `hello.c` というファイルをオブジェクトファイルにするときは `-c` オプションを使用する。

```shell-session
$ gcc -c hello.c
```
これを実行することで `hello.o` というオブジェクトファイルが作られる。

もちろん複数ファイルも指定できる。

```shell-session
$ gcc -c a.c b.c
```

これで `a.o` `b.o` ができた。

### 複数のオブジェクトファイルから実行ファイルを作る
`a.c` `b.c` `c.c` というソースコードから以下のコマンドを使用して `main` という実行ファイルを作るとする。

```shell-session
$ gcc a.c b.c c.c -o main
```

その後、 `a.c` を変更したので `main` を更新したくなった。
しかし、`main` を更新するために上記コマンドを実行すると変更していない `b.c` `c.c` も再びコンパイルすることになり二度手間である。

そういうときにはすでにコンパイル済みのオブジェクトファイルを使うことができる。

```shell-session
$ gcc a.c b.o c.o -o main
```

これで実行ファイル `main` ができた。

### 静的ライブラリをリンクする -L
静的ライブラリとは `libxxx.a` のようなファイルのこと。
オブジェクトファイルの集合体で、 `ar` コマンドを使用して以下のように作成する。

```shell-session
ar -r libsample.a a.o b.o c.o
```

※ `nm` コマンドとか使うと色々中身が見れる。aファイル作成前のoファイルと比較すると面白いかも?

静的ライブラリもオブジェクトファイルの集合体だから、実行ファイルを作成するときに使用することができる。
ただしコンパイル時に静的ライブラリがあるディレクトリを `-L` オプションで指定する必要がある。

例えば、

```
---
 |- a.c
 |- b.c
 |- libsample.a 
```

というディレクトリ構成で, `a.c` `b.c` `libsample.a` から実行ファイル `main` を作るときは以下のようにしてディレクトリと静的ライブラリを指定する。

```shell-session
$ gcc a.c b.c -L. -lsample -o main
```
静的ライブラリは `-lxxx` の形で指定する。`lib`、`.a` の部分は省略することができる。

（ここから少し怪しいです...）

-Lで静的ライブラリが存在するディレクトリを指定すると書いたが、 算術用ライブラリ `libm.a` は

```shell-session
$ gcc a.c -lm -o main
```

のように `-L` オプションなしで使用できる。
これは、リンカがデフォルトでライブラリを探しにいくディレクトリが決まっているからである。

これは `ld --verbose` でリンカスクリプトを見ると書いてある。

参考: [リンカの役割 自分メモメモ](https://qiita.com/saikoro-steak/items/e5089a2b33b52d5410bd)

### ヘッダを追加 -I
c/c++言語でヘッダファイルをインクルードする際には `#include` ディレクティブを使用する。

このときヘッダファイルの指定には以下の2つ方法がある。

```c:ヘッダファイルの指定方法
#include <stdio.h> 
#include "stdio.h"
```

`<...>` と `"..."` の違いはコンパイラがヘッダファイルを探しにいくディレクトリのパスである。

`<...>` はデフォルトで指定されているパスを参照してヘッダファイルを探す。
`"..."` はデフォルトで指定されているパスに加えて "..." 内に書かれたパスにもヘッダファイルを探しにいく。

コンパイラがデフォルトで参照するパスは `-v` オプションを使用して、コンパイル処理の各段階において実行されたコマンドを出力することによって確認するのが手っ取り早い。

例えば、次の出力は ubuntu 16.04LTS で `gcc -v xxx.c` を実行させたときの一部である。

```shell-session
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86-64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu

... 省略 ...

#include "..." search starts here:
#include <...> search starts here:
  /usr/include/c++/5
  /usr/include/x86_64-linux-gnu/c++/5
  /usr/include/c++/5/backward
  /usr/local/include

... 省略 ...
```

しかし、毎回ヘッダファイルの指定に `#include "..."` を使うのはパスをハードコーディングすることになり、色々面倒だ。

その時に使用するのが `-I` オプションである。
`-I` オプションを使用すると、 コンパイラが参照するパスに `-I` で指定したパスを追加してくれる。

次の出力は、 `gcc -I../ -v xxx.c` を実行したものである。

```
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86-64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu

... 省略 ...

#include "..." search starts here:
#include <...> search starts here:
  ../
  /usr/include/c++/5
  /usr/include/x86_64-linux-gnu/c++/5
  /usr/include/c++/5/backward
  /usr/local/include

... 省略 ...
```

しっかりと `<...>` にパスが追加されている。
これで `#include <...>` で自作のヘッダが追加できるようになった。

### -Wall, -W
* バグかもしれない部分に警告を出してくれる。
    * 潜在的なミスを指摘してくれるので基本的に付けておいた方が得な気がしている。
    * 参考: [gcc の警告オプション -Wall と -W](https://at-aka.blogspot.com/2006/11/gcc-wall-w.html)
    
### -std=c++11, 14, 17, ...
* それぞれ, c++11, c++14, c++17の機能を有効にする。
    * C++は3年に1回新しい国際基準が更新されるため、C++20なら, -std=c++20のように追加されていくでしょう。
    * 参考: [処理系](https://cpprefjp.github.io/implementation.html)

### -O1, -O2, -O3, -Os
* 最適化オプション
    * 参考: [最適化オプション](http://wwweic.eri.u-tokyo.ac.jp/computer/manual/altix/compile/Fortran/Intel_Fdoc91/main_for/mergedProjects/optaps_for/common/optaps_lev_opts.htm)
    * 参考: [プログラム高速化の禁術](https://qiita.com/kotauchisunsun/items/84e01c6fb621fcc1a647)

## まとめ
CMakeとかは偉大だなあって思う...