C/C++のライブラリについて

概要

  • 静的ライブラリ,動的ライブラリ(そんなものは無い.それは一般的には共有ライブラリ),共有ライブラリ,静的リンク,動的リンクなど,分かっているようで理解が曖昧な点を整理しておく.
  • C++ではヘッダファイルだけで提供するライブラリがある.ヘッダオンリーライブラリなどと呼ばれているようだ.それも簡単なメモを書いておく.

静的ライブラリと共有ライブラリ

  • 拡張子はそれぞれ,静的ライブラリは.a,共有ライブラリは.so.
  • コンパイル時の-lで指定するのは静的ライブラリも共有ライブラリも同じ.例えば,-lmはmathライブラリがリンクされる.ただし,デフォルトでは,まず共有ライブラリ(libhoge.so)を探して,見つかれば共有ライブラリがリンクされる.共有ライブラリがなければ,静的ライブラリ(libhoge.a)を探してリンクする.明示的に静的ライブラリをリンクしたい場合は,-staticオプションを付ける.
  • 静的ライブラリは単純には複数のオブジェクトファイルをまとめたもの.arコマンドで生成する.
  • 共有ライブラリは各オブジェクト生成時に-fPIC(Position Independent Code)を指定して,ライブラリ生成時に-sharedを指定する.

静的リンクと動的リンク

  • 静的リンクはライブラリがオブジェクトファイルに埋め込まれる.よって,ライブラリが無い環境でも動くバイナリを生成できる.しかし,ファイルサイズは大きくなる.
  • 反対に,動的リンクはオブジェクトファイルにはライブラリ自体は埋め込まれず,そこに埋め込めという情報だけが埋め込まれる.これは,ファイルサイズは小さくなるものの,ライブラリが無い環境では動かない.マシン上の複数のプログラムが同じ共有ライブラリを使う場合にもメモリの削減になる.libhoge.dllはダイナミックリンクライブラリであり,名前の通り実行時にリンクされる.
  • 共有ライブラリのリンクの際にライブラリを探索するパスはLD_LIBRARY_PATHである.共有ライブラリを使う場合は,このパスに対象のディレクトリが含まれていることを確認する.
  • 動的リンクは実行時に配置されるので,配置に依存しないコンパイル(PIC)をしておく

具体例

  • 自分のユーティリティライブラリ(myutil)を作る場合
///////////////////////////////////////////////////////////
// myutil.h
void hello_world(void);

///////////////////////////////////////////////////////////
// myutil.c
#include <stdio.h>
#include "myutil.h"

void hello_world(void) {
  printf("Hello world!! I'm a static library\n");
}

///////////////////////////////////////////////////////////
// main.c
#include <stdio.h>
#include "myutil.h"

int main()
{
  hello_world();
  return 0;
}

静的ライブラリとして使う場合

  • オブジェクトファイルを普通に作って,arでまとめて,リンクする.
$ gcc myutil.c -c             # myutil.oを生成
$ ar r libmyutil myutil.o     # libmyutil.aを生成
$ gcc main.c -L ./ -l myutil  # libmyutil.aをリンクしてコンパイル (libmyutil.soがない前提)
$ gcc -static main.c -L ./ -l myutil  # libmyutil.aをリンクしてコンパイル (libmyutil.soがある場合は明示)

共有ライブラリとして使う場合

  • -fPICで位置非依存なコードを作って,sharedでリンク
$ gcc -fPIC myutil.c -c                # myutil.oを生成
$ gcc -shared -o libmyutil.so myutil.o # libmyutil.soを生成
$ gcc main.c -L ./ -l myutil           # libmyutil.soをリンクしてコンパイル 

ヘッダオンリーライブラリ

  • ライブラリ提供者はヘッダファイルに全てを書いておいて,ライブラリ使用者はそのヘッダファイルを読み込む.
  • 毎回ヘッダファイルを読み込むのでコンパイル時間の増加の問題があるが,プリコンパイル済みヘッダファイルの利用によって避けることができる.
  • グローバル変数やスタティッククラス変数を定義しようとすると,複数のファイルが同じヘッダファイルを読み込むと多重定義エラーが発生する.避けるには,スタティックメンバ関数の中にスタティック変数として定義して,それを返す.