2013-08-31 15 views
18

を '呼び出し'する.cファイルで使用されているさまざまな関数、構造体などの前方宣言がヘッダファイルにあることは知っていますか?私の知る限り理解し、 "三権分立" は、このように発生します。Cヘッダファイルとコンパイル/リンク

ヘッダファイル:func.h

  • を機能

    int func(int i); 
    

Cソース・ファイルの前方宣言が含まれています。 func.c

  • は、実際の関数の定義に含まれてい

    #include "func.h" 
    
    int func(int i) { 
        return ++i ; 
    } 
    

Cソースファイルsource.c( "実際の" プログラム):

#include <stdio.h> 
#include "func.h" 

int main(void) { 
    int res = func(3); 
    printf("%i", res); 
} 

私の質問です:#includeは単にそのコピーコンパイラ・ディレクティブであることを見て#includeが入っているファイルの.hの内容は、どのようにして実際にその機能を実行するのかを知っていますか?.cそれはすべてint func(int i);なので、実際にどのように機能するのですか? funcの実際の定義にどのようにアクセスできますか?ヘッダーには、「そこには私の定義があります」と言う何らかの「ポインタ」が含まれていますか?

どのように動作しますか?

+0

これは、リンカの定義を解決し、コンパイル時に存在すると主張しているものが実際に存在することを保証する魔法です。 –

+0

ヘッダーファイルを扱うときは、[include guards](http://en.wikipedia.org/wiki/Include_guard)を参照してください。 –

+0

私はインクルードガードについて知っていますが(すべてのifndef)、簡潔にするため省略しました。 – Aristides

答えて

20

ウチア・イタチが答えを出しました。 リンカーです。

あなたの例で説明したように、あなたが

gcc hello.c -o hello # generating the executable hello 

ような1ファイルプログラムをコンパイルしますgcc GNU Cコンパイラを使用したが、2つ(またはそれ以上)のファイルプログラムをコンパイルすると、次の操作を実行する必要があります。

gcc -c func.C# generates the object file func.o 
gcc -c main.C# generates the object file main.o 
gcc func.o main.o -o main # generates the executable main 

各オブジェクトファイルには外部記号があります(パブリックメンバーと考えることができます)。関数はデフォルトでは外部変数であり、(グローバル)変数はデフォルトで内部変数です。あなたはとして提供される関数の呼び出し、メインモジュールで定義されていない、リンカを検索し、すべてのオブジェクトファイル(およびライブラリ)に遭遇すると

static int func(int i) { # static linkage 
    return ++i ; 
} 

または

/* global variable accessible from other modules (object files) */ 
extern int global_variable = 10; 

を定義することによって、この動作を変更することができます呼び出された関数が定義されているモジュールの入力。デフォルトでは、あなたのプログラムにリンクされているライブラリがあるかもしれません。それはprintfの使い方です。すでにライブラリにコンパイルされています。

本当に興味がある場合は、アセンブリプログラミングを試してみてください。これらの名前は、アセンブリコードのラベルに相当します。

+0

したがって、GCCのパターンは次のとおりです。1.それぞれの.c(定義付き)および.h(funcプロトタイプ付き)で-cフラグを使用して、それぞれを作成します。2. -oフラグと各.oファイルを使用して、最終的なexe? – Aristides

+0

はい。 "-c"オプションは "コンパイル"のためのものです。オブジェクトコードをオブジェクトファイルにコンパイルするだけです。 gccは-cを付けずに入力がオブジェクトファイルであることを認識し、リンカを使用してそれらをリンクします。最後に、-oフラグはオプションです。実行可能ファイルの出力ファイル名を指定するために使用されます。 –

+2

これは本当に良い答えです、ありがとうございます。 –

14

これをすべて処理するのはリンカです。コンパイラは、オブジェクトファイル内に "この外部シンボルfuncがあります。それを解決してください"という特別なシーケンスをリンカに対して出力します。リンカはそれを見て、シンボルの他のすべてのオブジェクトファイルとライブラリを検索します。

+0

これは、プロジェクト内のすべての '.c'ファイルが検索されることを意味しますか? –

+0

@LidongGuoすべてのソースファイルをコマンドラインで一緒にコンパイルする場合、またはすべてのソースからオブジェクトファイルを作成してそれらをすべてリンクする場合は、それらが検索されます。自動的には行われませんが、リンクするオブジェクトファイルをリンカーに伝える必要があり、それらのファイルのみが検索されます。 –

2

ヘッダーは、同じプログラム内の他の.cファイルにアクセスするだけでなく、バ​​イナリ形式で配布されるライブラリにもアクセスできます。 1つの.cファイルと別のファイルとの関係は、別のファイルに依存するライブラリとまったく同じです。

実装の形式にかかわらずプログラミングインターフェイスがテキスト形式である必要があるため、ヘッダーファイルは懸念の分離として意味があります。

他にも言及したように、関数呼び出しとライブラリとソース(翻訳単位)間のアクセスを解決するプログラムはリンカーと呼ばれます。

リンカーはヘッダーでは機能しません。これは、すべての翻訳単位とライブラリで定義されているすべての名前の大きなテーブルを作成し、それらの名前をアクセスするコード行にリンクします。古典的なCの使用法では、実装宣言なしで関数を呼び出すこともできます。定義されていないすべてのタイプがintであると仮定しました。

3

同じコンパイル単位内に定義のないシンボルを宣言すると、そのシンボルのアドレスのプレースホルダをオブジェクトファイルにコンパイルするようにコンパイラに指示されます。

リンカーは、シンボルの定義が必要であることを確認し、ライブラリやその他のオブジェクトファイル内のシンボルの外部定義を探します。

リンカが定義を検出した場合、元のオブジェクトファイルのプレースホルダは、最終実行可能ファイルの見つかったアドレスに置き換えられます。

1

一般的に次のようにファイルをコンパイルするとき:それは別々であるためにあなたが求めている場合(

  • 前処理:あなたは本当に、次の処理を行い、ドライバプログラムを、呼び出している

    gcc -o program program.c 
    

    ステップ)cppを使用します。

  • コンパイルas(ガス、GNUアセンブラ)を使用して、cc1
  • 組立を用いて(前処理と一体化することができます)。
  • collect2を使用してリンクしています。ld(GNUリンカー)も使用しています。

一般的に、最初の3つの段階で、あなたはコンパイル単位をコンパイルして作成される単純なオブジェクトファイル(拡張子.o)を作成(つまり、.Cファイルである、と置き換えの#includeや他のディレクティブとプリプロセッサ)。

第4ステージは、最終実行可能ファイルを作成するステージです。ユニットのコンパイル後、コンパイラはリンカによって解決される必要がある参照としていくつかのコードをマークします。リンカーの仕事は、多くのコンパイル単位を検索し、外部コンパイル単位への参照を解決することです。

関連する問題