.
コンパイラの主なワークフローは、ソースコード - プリプロセッサ - コンパイラ - オブジェクトコード - リンカ - 実行ファイルです。
ワークフローに関する別の視点:字句解析 - 構文解析 - 意味解析 - 中間コード生成 - コード最適化 - ターゲットコード生成 - ターゲットコード最適化
コンパイラの種類
”
コンパイラ自身が存在する環境と同じオペレーティング・システム上で動作するターゲット・コードを生成するコンパイラは、「ネイティブ」コンパイラと呼ばれます。
”
他のプラットフォームで動作するオブジェクトコードを生成するコンパイラは「クロス」コンパイラと呼ばれます。このプロセスはクロスコンパイルとも呼ばれます。
例えば、Android上で動作するsoをMac上でコンパイルするプロセスをクロスコンパイルと呼びます。
コンパイラのフロントエンドとバックエンド
中間コード生成ステップを中心に分割:
- ソース言語に関連し、ターゲット言語に関連しない部分をコンパイラ・フロントエンドと呼びます。
- ソース言語ではなく、ターゲット言語に関連する部分をコンパイラー・バックエンドと呼びます。
コンパイラをフロントエンドとバックエンドに分けたことが、コンパイル技術に一役買っています。
. GNU & GCC & Clang & llvm
GNU
GNU、Gnu's Not Unixの略。最初はUnixが商業的に利用可能であったため、リチャード・ストールマンはUnixに代わる完全なオープンソースのオペレーティングシステムを開発するためにGNUプロジェクトを立ち上げ、GNUと名付けました。
1989年、GNUプロジェクトはエディタ、コンパイラ、シェルなどを完成させましたが、オペレーティング・システムのカーネルだけが欠けていました。
1991年にLinuxが登場し、GNUプロジェクトのソフトウェアがLinux上で動くようになりました。
1992年、LinuxとGNUは組み合わされ、略称GNU/Linuxと呼ばれる完全に自由なオペレーティング・システムを形成しました。このとき、Hurdはまだ完成しておらず、放棄されました。
GCC
gccはGNU C Compilerの略で、GNUプロジェクトのコンパイラの一部であり、UnixライクおよびMac OS Xオペレーティング・システムの標準コンパイラです。
gccはもともとCだけを扱っていましたが、その後Object-c、Java、C++を扱えるように進化しました。
g++
gccとg++はどちらもGNUコンパイラです。両者の違いは以下の通りです:
- .cの場合、gccはCプログラムとして、g++はC++プログラムとして扱います。.cppの場合、gccもg++もC++プログラムとして扱います。
.cppのコンパイルとリンクは、gccとg++の両方で行うことができ、リンクはg++またはgcc -lstdc++で行うことができます。gccコマンドはC++プログラムで使用されるライブラリに自動的にリンクしないので、-lstdc++でリンクするのが一般的です。
Clang
Clangは、C、C++、Objective-C、Objective-C++プログラミング言語用のコンパイラ・フロントエンドです。バックエンドには仮想マシンを使用します。このソフトウェア・プロジェクトは、GNUのgccコンパイラ・スイートを置き換える目的でAppleによって開始されました。
gccコンパイラは徐々にAppleのニーズを満たすことができなくなったため、Appleはgccを完全に置き換えるためにClangとLLVMを開発し、Xcode4以降、AppleのデフォルトのコンパイラはClang/LLVMになりました。 Clangはコンパイラのフロントエンドとして、LLVMはコンパイラのバックエンドとして使用されます。
MinGw
MinGwはMinimalist GNU for Windowsの略です。GNUツールセットを使ったWindows固有のヘッダファイルとインポートライブラリのコレクションで、Windowsプラットフォーム上で、サードパーティのCランタイムライブラリを使うことなく、ネイティブなWindowsプログラムを生成することができます。
. C/C++ コンパイラ
C/C++のコンパイル・プロセスは、4つのステップに分けることができます:
- 前処理
- コンパイル
- アセンブリ
- リンク
前処理
プリプロセッサーとは、通常#で始まる処理ファイル内の前処理コマンドのことです。 プリプロセッサーは通常、以下のステップで構成されます:
- マクロ定義の置き換え#define
- if のような条件付きプリコンパイルディレクティブの処理
- インクルードする必要があるファイルを再帰的にインクルードする#includeディレクティブを扱います。
- ちょっと待ってください!
Eコマンドで前処理を行い、-oコマンドで生成されたファイルを示し、最終的に.iファイル
gcc -E -o test.i test.c
例えば.h test.c
>>test.h
int func(int a,int b) {
return a + b;
}
>>test.c
#include "test.h"
#define A 1
#define B 2
int main() {
int c = func(A,B);
}
gcc -E -o testを実行する。.i test.c
>> 生成テスト.i
# 1 "test.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 363 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "test.c" 2
# 1 "./test.h" 1
int func(int a,int b) {
return a + b;
}
# 3 "test.c" 2
int main() {
int c = func(1,2);
}
コンパイル
コンパイルのプロセスは、処理されたプログラムを特定のアセンブリ言語コードに変換するプロセスです。 コンパイル操作を実行するには -E コマンドを使用します。これは、コンパイラがコンパイル後に停止し、次のステップに進まないことを意味します。
gcc -S -o test.s test.c
>> アセンブリコード:
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 10, 15 sdk_version 10, 15, 4
.globl _func ## -- Begin function func
.p2align 4, 0x90
_func: ## @func
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
addl -8(%rbp), %eax
popq %rbp
retq
.cfi_endproc
## -- End function
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl $1, %edi
movl $2, %esi
callq _func
xorl %ecx, %ecx
movl %eax, -4(%rbp)
movl %ecx, %eax
addq $16, %rsp
popq %rbp
retq
.cfi_endproc
## -- End function
.subsections_via_symbols
アセンブリ
アセンブリは上記のアセンブリコードをマシンコードに変換することで、このステップで生成されるファイルはターゲットファイルと呼ばれ、バイナリ形式です。すべてのソースファイルは.oターゲットファイルを生成します。
as test.s -o test.o
gcc -c test.c -o test.o
>> ここには掲載しない。
リンク
リンクプロセスとは、ターゲットファイルと必要なライブラリファイルをリンクして、最終的な .out 実行ファイルにするプロセスです。リンカの主な仕事は、.o ターゲットファイル同士をリンクして、オペレーティングシステムで読み込んで実行できるようにすることです。
>>
gcc test.c
を生成する。.out 異なるオペレーティングシステムにおけるコンパイル済みプロシージャーファイルの接尾辞
マックでの実行./a.out 実行可能ファイル
IV. 静的リンクと動的リンク
静的リンク
静的リンク:リンク段階では、アセンブリによって生成されたターゲット.oファイルがリンクされ、参照されるライブラリとともに実行ファイルにパックされます。つまり、必要な実行コードはコンパイルとリンクの間に呼び出し元に直接コピーされるため、許可されている場合は複数のメモリコピーが存在します。
スタティック・ライブラリ:ターゲット・ファイルのコレクション、つまり、多数のターゲット・ファイルを圧縮してパッケージ化したファイル。
静的リンクライブラリの特徴
- スタティック・ライブラリとファンクション・ライブラリのリンクはコンパイル時に行われます。
- ライブラリーに依存しないプログラム
- メモリの無駄遣い、関数が複数の場所から呼び出されると複数のコピーが存在します。
ダイナミック・リンク
つまり、コンパイル時に実行コードを直接コピーするのではなく、一連のシンボルとパラメータを記録し、プログラムの実行またはロード時にこの情報をオペレーティング・システムに渡し、オペレーティング・システムは必要なダイナミック・ライブラリをメモリにロードする役割を果たします。
その後、プログラムが指定されたコードまで実行されると、メモリにロードされているダイナミック・ライブラリの実行可能コードの実行を共有するようになり、最終的にランタイム・コネクションの目的が達成されます。
ダイナミックリンクライブラリの特徴
- ダイナミック・ライブラリは、一部のライブラリ関数へのリンクのロードを、プログラムが実行されるまで遅らせます。
- 複数のプログラムは、メモリに複数のコピーを保存することなく、同じコードを共有することができます。
- 欠点は、実行時にロードされるため、プログラムの実行前のパフォーマンスに影響を与える可能性があることです。





