web-dev-qa-db-ja.com

静的ライブラリからヘッダーファイルを含める

Cの静的ライブラリとプログラムのテストセットアップを作成しています。プロジェクトのサブディレクトリ「foo」にあるライブラリコードには、次のファイルが含まれています。

foo/foo.c:

#include <stdio.h>
void foo(void) {
    printf("something");
}

foo/foo.h:

#ifndef foo_h__
#define foo_h__
extern void foo(void);
#endif

私のプログラムコードは次のとおりです。

test.c:

#include "foo.h"
int main() {
    foo();
    return 0;
}

'build'と呼ばれるビルドスクリプトがあり、以下が含まれています。

ビルド:

#!/bin/bash
gcc -c -Wall -Werror foo/foo.c
ar rcs libfoo.a foo.o
gcc -static -o test test.c libfoo.a # I have also tried -L. -lfoo

しかし、ビルドを実行すると、次のエラーが発生します。

test.c:1:17: fatal error: foo.h: No such file or directory
  #include "foo.h"
                  ^
Compilation terminated

ただし、#include行を省略した場合は機能しますが、静的ライブラリでヘッダーファイルを使用できると便利です。何が悪いのですか、どうすれば修正できますか?

14
Ethan McTague

ヘッダーはライブラリに保存されません。ヘッダーはライブラリとは別に保存されます。ライブラリにはオブジェクトファイルが含まれます。ヘッダーはオブジェクトファイルではありません。デフォルトでは、Unixシステムの標準ヘッダーは/usr/includeに保存されます。たとえば、通常は/usr/include/stdio.h/usr/include/string.h/usr/include/stdlib.hなどがあります。デフォルトでは、ライブラリは/usr/libに格納されています(ただし、/libにもライブラリが含まれている場合があります)。多くの場合、コンパイラは他の場所も検索するように構成されています。一般的な代替の場所の1つは/usr/localの下にあるため、ヘッダーには/usr/local/include、ライブラリには/usr/local/libがあります。また、単一のライブラリーには、サービスを定義する多くのヘッダーがある場合があることに注意してください。デフォルトのライブラリは一例です。 <stdio.h><string.h><stdlib.h>およびその他の多くのヘッダーにある関数に対応しています。

コードを見る:

  1. ヘッダーファイルが./foo/foo.hにある場合は、次のように記述する必要があります。

    #include "foo/foo.h"
    

    または、#include "foo.h"を引き続き使用する場合は、次の引数を使用して、コンパイラのコマンドラインでヘッダーを検索する場所を指定する必要があります。

    gcc -Ifoo -o test test.c -L. -lfoo
    

    -staticを意図的に除外しました。静的ライブラリと共有ライブラリのどちらかを選択できる場合にのみ必要ですが、libfoo.aしかないため、リンカはとにかくそれを使用します。

    問題はリンクエラーではなく、コンパイルエラーであることに注意してください。プログラムのビルドを2つのステップに分割すると、これはより明確になります。(1)test.oを作成し、(2)プログラムをリンクします。

    gcc -c -Ifoo test.c
    gcc -o test test.o -L. -lfoo
    
  2. ヘッダーガードに欠陥があります。あなたはもともと持っていました(しかし質問を更新したのでこのタイプミスはもうありません):

    #ifndef foo_h__
    #define foo_h_
    

    必要なもの:

    #ifndef foo_h__
    #define foo_h__
    

    マクロ名は両方の行で同じでなければなりません。この場合、スペルミスはほとんど無害です。ただし、Mac OS Xでは、clanggccになりすます)は警告を発しました(以前に見つけたはずですが)コンパイル)。他のいくつかのケースでは、ヘッダーガードが提供するように設計されている保護を取得できません。

    ./foo/foo.h:1:9: warning: 'foo_h__' is used as a header guard here, followed by #define of a
          different macro [-Wheader-guard]
    #ifndef foo_h__
            ^~~~~~~
    ./foo/foo.h:2:9: note: 'foo_h_' is defined here; did you mean 'foo_h__'?
    #define foo_h_
            ^~~~~~
            foo_h__
    1 warning generated.
    

あなたは合法的に不思議に思うかもしれません:

  • -Ifooをコンパイルするときにtest.cが必要なのに、foo/foo.cをコンパイルするときになぜ必要なかったのですか?

良い質問!

  1. foo/foo.cのコンパイルに支障はありません。
  2. GCCは、翻訳単位のソースコードが見つかったディレクトリでヘッダーを探します(したがって、foo/foo.cをコンパイルするときに、fooディレクトリで#include "foo.h"として含まれているヘッダーを探します。
  3. ソースファイルfoo/foo.cにもfoo.hが含まれているはずです。コンパイラーが一貫性を確保するために必要なクロスチェックを提供する方法であるので、それを行うことが非常に重要です。 #include "foo.h"を記述した場合、コンパイルは説明どおりに機能します。 (foo/foo.cで)#include "foo/foo.h"と書いた場合、foo.oを作成するためのコマンドラインは-I.を必要とするため、ヘッダーが見つかります。
28