C++を書くうちに、そういえばリンカとかライブラリの仕組みを理解していなかったな、と思ったので色々調べてみました。書きやすいからという理由で説明口調の文章にしていますが、多分に間違いを含む可能性があります!(オイ)気付いた部分があればコメントしていただけるとありがたいです。ビルドとはまずビルドとはなんでしょうか?コンピュータの世界において、ビルドは材料となるファイルから何らかの成果物を作成する処理を意味します。C/C++のようなプログラム言語のビルドの場合、材料はソースコード、成果物はライブラリや実行ファイルのことを指します。ライブラリって何?という話についてはこの記事の後半で解説します。次の図を使いながら説明していきます。C/C++のビルド処理はプリプロセッサ・コンパイラ・アセンブラ・リンカという4つのパートから成ります。プリプロセッサプリプロセッサとは名前の通りコンパイルのための下準備をするパートのことです。ビルドを開始すると、まずプリプロセッサがソースファイル(*.c/*.cppなど)1つ1つに対して次のような処理を行います。#include/#defineなどのプリプロセッサディレクティブの解決コメントの削除ここで特に重要なのは#includeの解決です。ソースファイルの中で#includeが指定されていた場合、プリプロセッサは指定されたヘッダファイルを探してきて、ソースファイルのその位置に中身をまるっと貼り付けます。*.c/*.cppのようなソースファイル中の#includeの部分には*.h/*.hppの中身が丸コピされるということです。この処理は再帰的に実行されるので、インクルードしたヘッダファイルに#includeがあればそのファイルの中身もコピーされます。再帰の中で同じヘッダファイルが何度か出てくることも想定されます。<vector>があちこちのヘッダファイルに書かれている、なんてことはよくあると思います。このとき同じヘッダファイルを何度もインクルードしてしまうのは効率が悪いです。onceというキーワードを追加することで1度しかインクルードしないようコンパイラに指示するのがC/C++の作法となっています。コンパイラプリプロセッサを通過したソースファイルはコンパイラに渡され、コンパイルが実行されます。コンパイルは「人間が読み書きやすいように設計された言語(高水準言語)のコードをコンピュータが理解しやすい形式に翻訳する処理」を意味します。今回のケースではC/C++で書かれたファイルをアセンブラ言語のファイルに変換する処理のことです。アセンブラ言語とは機械語を扱いやすいように書き直した低水準言語です。コンパイラはプリプロセッサから受け取ったソースファイル1つ1つをアセンブラ言語のファイルに変換します。先ほど「コンパイラはソースファイルをそれぞれアセンブラ言語のファイルに変換する」と説明しました。ここでソースファイルを跨いで参照されている関数はどうやって処理されるのかという問題が生じます。次のようなソースコードをビルドするとしましょう。0;}このとき、main.cをコンパイルする段階ではmylib.cにあるadd関数の実装は見えていません。main.cとmylib.cは別々にコンパイルされるためです。それでもちゃんとmain.cのコンパイルに成功します。実は、この問題を解決するための仕組みがヘッダファイルとプロトタイプ宣言です。int);のようにadd関数のプロトタイプ宣言が書かれています。mylib.hはプリプロセッサの段階でmain.cに丸コピされるので、main.cのコンパイル時には「int型の引数を2つ取ってint型の値を返すaddという関数がある」という情報が得られることになります。コンパイラはその手がかりだけを基にコンパイルを完了させ、addの実装の中身は保留します。して他のファイルで定義されている関数の実装を保留した状態(外部参照が未解決であるといいます)のアセンブラ言語のファイルがソースファイルの数だけ作成されます。アセンブラコンパイラで生成されたアセンブラ言語のファイルを機械語のファイルに変換するのがアセンブラです。ここでCPUのアーキテクチャに合わせた機械語が生成されます。ちなみに、生成される機械語のファイルはオブジェクトファイルまたは、機械語が2進数の命令の羅列であることからバイナリファイルと呼んだりします。オブジェクトファイル内の機械語に翻訳されたプログラムはオブジェクトコードと呼ばれます。リンカ最後に作成された複数のオブジェクトファイルを1つにまとめる作業をリンクといいます。これを実行するのがリンカです。リンカはmain関数のあるオブジェクトファイルから芋づる式に、保留された関数の実装(未解決の外部参照)を探索していきます。必要な情報を全て集めて1つの実行ファイルないしライブラリファイルを作成します。ここまでの処理が滞りなく終われば晴れてビルド完了となります。ライブラリの種類と仕組みここまでC/C++のビルドの仕組みを説明してきました。ファイルを1つ1つコンパイル・アセンブルしてからリンクするこの仕組みは一見ムダが多いように思えますが、ライブラリの概念を導入するときに威力を発揮します。ライブラリとはビルドの管理を楽にしたり他人の書いたプログラムを再利用したりするために、プログラムをいくつかの単位に分割する仕組みのことです。stdio.hやstdlib.hなどの標準ライブラリもこの仕組みを利用しています。ここからはこのライブラリの仕組みについて説明していきたいと思います。単純なライブラリの形式は、ソースコードをそのまま配布するというものです。プログラムを分割していないのでライブラリと呼べるか微妙なところですが、他人が書いたプログラムを再利用しているのでとりあえずライブラリの一種ということにしておきます。自分が書いたソースコードと全く同じようにビルドします。library)といいます。通常は複数のオブジェクトファイルをまとめた静的リンクライブラリファイル(Windowsでは*.lib、OSX/Linuxではlib*.a)の形で配布されます。静的ライブラリを自分のプログラムに組み込むためには、ビルドのときにヘッダファイルと静的リンクライブラリファイルが必要になります。自分のソースコードにこのヘッダファイルを#includeさせ、リンクのタイミングで静的ライブラリを混ぜ込むようコンパイルオプションを設定してビルドします。するとライブラリのコードも内包した実行ファイルが作成されます。一例としてgccでlibmylib.aを混ぜながらmain.cをビルドするときのコマンドを紹介しておきます。mylib指定すべき要素は1ライブラリのヘッダファイルの場所、2ライブラリファイルの場所、3ライブラリの名前の3つです。Studioの場合は、プロジェクトのプロパティ内の1C/C++>追加のインクルードディレクトリ、2リンカ>追加のライブラリディレクトリ、3リンカ>入力>追加の依存ファイル、の設定項目がそれぞれ対応しています。linking)静的ライブラリを使ったパターンでは全てのコードを内包した実行ファイルが作成されるのでした。これに対しリンクが不完全な実行ファイルを作成しておいて実行時にリンクを行う方式を動的リンクといいます。library)もしくは共有ライブラリと呼ばれます。コンパイラのオプションを適当に指定してビルドすると動的リンクライブラリファイルが作成されます(Windowsでは*.dll、OSXではlib*.dylib、Linuxではlib*.so)。設定すべきオプションは開発環境によってまちまちなので、ここでは紹介を省きます。動的リンクライブラリを自分のプログラムから利用するときはヘッダファイルと動的リンクライブラリファイルが必要になります。自分のソースコードにこのヘッダファイルを#includeさせ、先ほど静的リンクのところに書いたのと同じ設定をしてビルドします。すると一部のコードを内包しない代わりに動的リンクの情報を持った実行ファイルが作成されます。この実行ファイルが起動され、動的リンクになっている箇所を実行することになった段階で、動的リンクライブラリファイルの探索とメモリへのロードが行われ、ライブラリ側のコードが実行されます。実行時に動的リンクライブラリを発見できるようにライブラリファイルへのパスを指定しておく必要があります(Windowsの場合は実行ファイルのあるディレクトリも探索するので、ここにdllをコピーするだけでもOKです。コピーしてしまうと以下で説明するメモリ使用量のメリットが消えますが...)。動的ライブラリを探索するときの細かい挙動はちょっと検証しきれていないのでこれくらいの表現に留めておきます、ごめんなさい。調べて別の記事で書くかもしれません。*.so)の組み合わせを配布していることが多いです。しておけば、ビルドの際には一律に静的ライブラリだけ読み込めばいいことになるからです。この静的ライブラリは処理の実装を持っていないので、実行時に動的リンクが行われます。loading)動的読み込みは動的リンクを拡張したようなライブラリの形式です。動的リンクでは、実行ファイルのコンパイルの時点でヘッダファイルと動的リンクライブラリファイルが必要でした。動的読み込みではビルドの段階ではライブラリ関連のファイルを一切必要としません。そのかわりに自分のソースコードの中で明示的に「xxxという名前の動的リンクライブラリの中のyyyという関数を呼び出す」という風に記述しておきます。することでライブラリファイルを後付けで動的リンクさせることができます。多くの環境ではdlfcn.hを通して動的読み込みの機能が提供されています。ライブラリ側は動的リンクライブラリファイルをそのまま使用できます。利点ライブラリファイルの探索やロードの処理もプログラム上で細かく実装できる実行ファイルのコンパイルの時点でライブラリ側が完成していなくてもよいこれらの利点から、プラグイン機能の開発に利用されることが多いです。実行ファイルの作成の時点ではライブラリの名前と関数名が文字列として埋まっているだけなのでコンパイル時の型チェックの恩恵を受けられない型や関数名の管理を手動でしなければならない以上、C/C++のビルドとライブラリの仕組みについてまとめてみました。こんなに書くはずじゃなかったんですが、気付いたらめちゃくちゃ長くなってました。(14)このブログについてプライバシーポリシー/免責事項お問い合わせはてなブログをはじめよう!kamino-devさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。