lib.*.aのようなバイナリファイル(静的リンクライブラリ)について、ビルドの際にこれらのファイルを指定すると同時に対応するヘッダファイルを別途指定する必要があるのはなぜですが?
リンカで複数のバイナリファイルを作るのが普通になった理由としては「逐一同じコード(ライブラリのコード部分)をコンパイルしているのが勿体ない」と言うのがあると思うのですが、なぜ与える必要があるのでしょうか。
どうせ後からライブラリの部分はリンクされるので、ヘッダファイルを与えてしまってコンパイルさせるのは無駄ではありませんか?
https://kamino.hatenablog.com/entry/c%2B%2B-principle-of-build-library
ここのブログを読ませていただいて思いました。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
質問の趣旨を捉えられているかどうかちょっと自信がないのですが、
ライブラリファイルはリンクに必要な情報を持っているはずなのに、なぜ同様な情報を持った「ヘッダファイル」を別扱いにしてインクルードとかいうオマジナイをしなきゃいけないのか、ということでしょうか。
であるなら、私の思うところでは、
C言語が古いものだから
です。
Cが生まれたのは1970年代前半。そのころのコンピュータ事情を考えてみて下さい。
ソースにオブジェクトにライブラリ、そんな沢山のファイルを一気にまとめて置けるストレージも、それを受け入れるメモリも、一般で手に入るようなコンピュータは持っていませんでした。(そもそもコンピュータが「一般」で手に入ったかどうか)。そんな環境でCは頑張ったのです。関数や変数は、その情報だけあれば中身全部がソースになくても外部結合という形で部分毎に処理出来るようにしました。プリプロセス/コンパイル/アセンブル/リンクを各ステージ分けることで、各ステージごとのメモリ使用量も切り詰められるでしょう。
コンピュータの能力が足りなければ、そう、プログラマが代わりに苦労すればいいのです。複数のソースで共用する情報は、人間がコンパイル時に必要なものだけ切り出してヘッダファイルにしましょう。リンカはコンパイル結果のオブジェクトとライブラリだけ寄せ集めれば出来るようにしました。ライブラリの情報とヘッダファイルの同期? そんなのプログラマの責任で管理して下さい。
そんな匠の心を今に伝えているのが「ヘッダファイル」なのではないかと。
今どきの言語は、関連するファイルを全部どこかにまとめておけば、ファイル間で共有すべき情報は処理系が勝手に抽出してくれるのが普通ですよね。時代は変わったものです。
投稿2020/05/27 13:09
総合スコア7703
0
えっと、OSがどうやって実行可能ファイルを実行しているかですよね。OSはローダによって実行可能ファイルをメインメモリに読み込みます。そのときローダはプログラムが正しく動作するようにプログラムを「改変」します。C言語はコンパイラとリンカによって実行可能ファイルを生成することは分かっていますよね。コンパイラが生成するファイルのことをオブジェクトファイルと呼ぶことも。要はこういうことです。
ソースプログラム(.c) → [コンパイラ] → オブジェクトファイル(.o) → [リンカ] → 実行可能ファイル → [ローダ] → 実際に動作するコード
リンカとローダはシンボルを編集します。オブジェクトファイルと実行可能ファイルと実際に動作するコードはほとんど同じものです。何が違うかというと、実際に動作するコード以外はシンボルという「断片をつなぎ合わせるのに必要な情報」を持っているということです。
C言語はソースファイル単位で機械語を生成します。特殊なビルドチェーンでなければリンクのときに最適化を行いません。なぜそうするかというとコンパイルという処理がとても重いためです。コンパイルはソースファイルを読み込んで機械語に変換する作業です。できればこの作業は小分けで行いたいのです。したがってオブジェクトファイルというファイル形式が存在するのです。コンパイラは関数名をシンボルに変換します。ソース内で他のソースファイルの関数を呼び出している場合は「これこれのシンボルください」という情報に変換されます。
ヘッダファイルは複数のソースプログラムで関数宣言を共有するためのものです。これはC言語に型という概念が存在するために必要です。他のソースプログラムの関数を呼び出すとき、正しく呼び出すためにはその関数のインターフェースが必要になります。ここでいう「インターフェース」はJava言語のインターフェースと似ています。Javaのインターフェースはクラスによって実装されます。Cのヘッダファイルはソースプログラムによって実装されるのです。
静的リンクライブラリがどうして存在するかというと、コンパイル済みのコード片が必要だからであり、静的リンクライブラリはリンカによってオブジェクトファイルを集約したものになります。ここで再びリンカを使うことによってユーザーが書いたオブジェクトファイルと静的リンクライブラリをリンクさせることができます。
どうしてC言語ではヘッダファイルと静的リンクライブラリの2種類を用意しなければならないかということですが、C言語がアセンブリ言語の代替として登場したというのもあります。オブジェクトファイルはC言語だけでなくアセンブリコードからも生成されます。そこで静的リンクライブラリはアセンブリ言語と互換性のある部分であり、ヘッダファイルは純粋にC言語固有の部分なのです。
投稿2020/06/10 16:17
編集2020/06/10 16:46総合スコア667
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
すでに出ていますが、関数の呼び出しにはその関数の戻り地・引数を定義した宣言が必要になります。
また、構造体・クラスを使用するならば、それらの定義も必要になります。
ライブラリ側のヘッダには、それらが記述されています。
もしヘッダファイルを提供しないとなれば、自分で記述しなければなりません。
例えば、以下の一般的なWin32のプログラム。
c
1#include <windows.h> 2 3LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 4{ 5 switch (msg) { 6 case WM_DESTROY : 7 PostQuitMessage(0); 8 break; 9 10 default : 11 return DefWindowProc(hWnd, msg, wParam, lParam); 12 } 13 return 0l; 14} 15 16int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPTSTR pszCmdLine, int nCmdShow) 17{ 18 WNDCLASSEX wcx; 19 HWND hWnd; 20 MSG msg; 21 22 wcx.cbSize = sizeof(WNDCLASSEX); 23 wcx.style = CS_HREDRAW | CS_VREDRAW; 24 wcx.lpfnWndProc = WindowProc; 25 wcx.cbClsExtra = 0; 26 wcx.cbWndExtra = 0; 27 wcx.hInstance = hInst; 28 wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION); 29 wcx.hCursor = LoadCursor(NULL, IDC_ARROW); 30 wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 31 wcx.lpszMenuName = NULL; 32 wcx.lpszClassName = _T("MainWndClass"); 33 wcx.hIconSm = NULL; 34 if (RegisterClassEx(&wcx) == 0) { 35 return 1; 36 } 37 38 hWnd = CreateWindowEx(0, _T("MainWndClass"), _T("Hello"), WS_OVERLAPPEDWINDOW, 39 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 40 NULL, NULL, NULL, NULL); 41 if (hWnd == NULL) { 42 return 1; 43 } 44 ShowWindow(hWnd, nCmdShow); 45 UpdateWindow(hWnd); 46 47 while (GetMessage(&msg, NULL, 0, 0)) { 48 TranslateMessage(&msg); 49 DispatchMessage(&msg); 50 } 51 52 return msg.wParam; 53} 54
この中の、
c
1#include <windows.h>
は、Microsoftが提供しているヘッダファイルです。
もしMicrosoftがヘッダファイルを提供しないとなれば、この一文の代わりに、以下のコードを自分で書く必要があります。
c
1 2#define NULL ((void *)0) 3#define _T(n) L##n 4typedef long long LRESULT; 5#define CALLBACK 6struct HWND__ { int unused; }; typedef struct HWND__ * HWND; 7typedef unsigned int UINT; 8typedef unsigned long long WPARAM; 9typedef long long LPARAM; 10typedef unsigned long DWORD; 11typedef unsigned short WORD; 12typedef long LONG; 13typedef void *LPVOID; 14#define WM_DESTROY 0x0002 15void PostQuitMessage(int); 16LRESULT DefWindowProcW(HWND, UINT, WPARAM, LPARAM); 17#define DefWindowProc DefWindowProcW 18#define APIENTRY 19struct HINSTANCE__ { int unused; }; typedef struct HINSTANCE__ *HINSTANCE; 20typedef wchar_t * LPTSTR; 21typedef LRESULT ( *WNDPROC)(HWND,UINT,WPARAM,LPARAM); 22struct HICON__ { int unused; }; typedef struct HICON__ *HICON; 23struct HBRUSH__ { int unused; }; typedef struct HBRUSH__ *HBRUSH; 24struct HMENU__ { int unused; }; typedef struct HMENU__ *HMENU; 25typedef HICON HCURSOR; 26typedef const wchar_t * LPCWSTR; 27typedef struct tagWNDCLASSEXW { 28 UINT cbSize; 29 UINT style; 30 WNDPROC lpfnWndProc; 31 int cbClsExtra; 32 int cbWndExtra; 33 HINSTANCE hInstance; 34 HICON hIcon; 35 HCURSOR hCursor; 36 HBRUSH hbrBackground; 37 LPCWSTR lpszMenuName; 38 LPCWSTR lpszClassName; 39 HICON hIconSm; 40} WNDCLASSEXW,*PWNDCLASSEXW,*NPWNDCLASSEXW,*LPWNDCLASSEXW; 41typedef struct tagPOINT { 42 LONG x; 43 LONG y; 44} POINT,*PPOINT,*NPPOINT,*LPPOINT; 45typedef struct tagMSG { 46 HWND hwnd; 47 UINT message; 48 WPARAM wParam; 49 LPARAM lParam; 50 DWORD time; 51 POINT pt; 52} MSG,*PMSG,*NPMSG,*LPMSG; 53typedef struct tagPOINT { 54 LONG x; 55 LONG y; 56} POINT,*PPOINT,*NPPOINT,*LPPOINT; 57typedef WORD ATOM; 58ATOM RegisterClassExW (const WNDCLASSEXW *); 59#define RegisterClassEx RegisterClassExW 60HWND CreateWindowExW(DWORD dwExStyle,LPCWSTR lpClassName,LPCWSTR lpWindowName,DWORD dwStyle,int X,int Y,int nWidth,int nHeight,HWND hWndParent,HMENU hMenu,HINSTANCE hInstance,LPVOID lpParam); 61#define CreateWindowEx CreateWindowExW 62#define WS_OVERLAPPEDWINDOW (0x00000000l | 0x00C00000l | 0x00080000l | 0x00040000l | 0x00020000l | 0x00010000l) 63#define CW_USEDEFAULT ((int)0x80000000) 64#define CS_HREDRAW 0x0002 65#define CS_VREDRAW 0x0001 66HICON LoadIconW(HINSTANCE hInstance,LPCWSTR lpIconName); 67#define LoadIcon LoadIconW 68HCURSOR LoadCursorW(HINSTANCE hInstance,LPCWSTR lpCursorName); 69#define LoadCursor LoadCursorW 70#define IDI_APPLICATION ((LPSTR)((ULONG_PTR)((WORD)(32512)))) 71#define IDC_ARROW ((LPSTR)((ULONG_PTR)((WORD)(32512)))) 72#define COLOR_WINDOW 5 73typedef int WINBOOL; 74WINBOOL ShowWindow(HWND hWnd,int nCmdShow); 75WINBOOL UpdateWindow(HWND hWnd); 76WINBOOL GetMessageW(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax); 77#define GetMessage GetMessageW 78WINBOOL TranslateMessage(const MSG *lpMsg); 79LRESULT DispatchMessageW(const MSG *lpMsg);
どうですか、あなたはこれを書く気になりますか?
私は、書くんじゃなかったと後悔してます(笑)
(チクショウ、「gcc -E
」でプリプロセッサ展開して一ファイルにすれば楽に書けるだろうとたかを括った)
投稿2020/05/27 14:22
総合スコア3538
0
ベストアンサー
補足的回答を。
ライブラリの配布方法はいくつかあります。
ヘッダーオンリーにしてしまう場合
この場合は利用者は単にヘッダーファイルをgit submoduleなりcmakeなり自力で引っ張ってきてinclude pathを通してincludeすれば利用できます。
C++のtempalteを多用するライブラリによく見られます。
利用しやすい半面、ビルド速度が遅くなりがちです。
ヘッダーオンリーにしない場合
ソースコードを配布する場合
ビルドのためのスクリプト(autotoolsの設定とかcmakeの設定とか)くらいはつけるけど、
をそのまま配布する場合。
利用者は予めビルドする必要があり、やや利用しにくいです。著名なソフトウェアならライブラリ作者ではなく第三者がビルドして公開している場合もあります。pacman, apt, yumなどの
パッケージマネージャなんかはその例です。またビルドスクリプトをパッケージにする例(vcpkgなど)もあります。
ビルド済のライブラリを配布する場合
静的/動的リンクライブラリとヘッダーファイルを配布します。
利用者はビルドする必要がありませんが、自分の環境向けのものが配布されていない場合はあります。またライブラリ製作者は必要な環境すべてに対してビルドしておかなければなりません。
ソースコードを開示する必要がないので、プロプライエタリソフトウェアなどに見られる他、利用者の多い環境向けに配布しているOSSもあります。
投稿2020/05/27 08:54
総合スコア5852
0
どうせ後からライブラリの部分はリンクされるので、ヘッダファイルを与えてしまってコンパイルさせるのは無駄ではありませんか?
いえ、ヘッダファイルがないと型情報が得られないため、コンパイルができません。
Javaや.NET Frameworkのような環境では、型情報もコンパイル後のオブジェクトファイルに保持されていますが、CやC++の場合は(デバッグ用は別として)コンパイル後のライブラリは実行コードだけで型情報が残らないので、ヘッダが必要です。
投稿2020/05/27 07:34
総合スコア146018
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/05/27 07:48
2020/05/27 07:59 編集
退会済みユーザー
2020/05/27 08:07
2020/05/27 08:31
2020/05/27 17:57
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。