3
// File foo1.c :
#include <stdio.h> // once
void foo1(void);
void foo1(void){
    puts("foo1");
}

// File foo2.c :
#include <stdio.h> // again
void foo2(void);
void foo2(void){
    puts("foo2");
}

// File foomain.c :
#include <stdio.h> // yet again
void foo1(void); // again
void foo2(void); // again
int main(void){
    foo1();
    foo2();
    puts("foomain");
    return 0;
}

// create object files
gcc -fPIC foo1.c -o foo1.o // 1 stdio.h
gcc -fPIC foo2.c -o foo2.o // 1 stdio.h

// create shared library
gcc -fPIC -shared foo1.o foo2.o -o foo.so // foo.so contains stdio.h 2 times ?

// build entire program
gcc foo.so foomain.c -o foomain // foomain contains 1 stdio.h plus the 2 from foo.so ?
  1. Why does the entire program contain 3 stdio.h ? Seems redundant, why not just 1 ? Shouldn't the compiler need only 1 ?

  2. It makes sense for the object files to contain a prototype but why do they have to be specified again in foomain.c ? Shouldn't the compiler know they are already specified in foo.so ?

4

4 に答える 4

8

これは、各ファイルが個別にコンパイルされるためです。そのため、コンパイラは、コンパイル時のチェックを実行するために使用されるすべての関数のシグネチャを毎回認識している必要があります。したがって、各ファイルには、ファイルがコンパイルされる前にプリプロセッサによって含まれる、使用されるすべての宣言が含まれている必要があります。

于 2012-04-23T10:10:54.613 に答える
3

ほとんどのヘッダーファイルの上部を見ると、二重インクルードを停止するためのインクルードガードがあります。

#ifndef FOO
#define FOO

#endif

詳細については、ガードを含めるを参照してください。

于 2012-04-23T10:08:28.097 に答える
2

#include行は実際にはコンパイラの一部ではなく、C プリプロセッサの一部です。

プリプロセッサが#include行に対して行うことは、実際にファイルをソースにインクルードし、ファイルの内容を含む新しい一時ファイルを作成し、その#include行をインクルードするファイルの内容に置き換えます。

関数を呼び出すだけであれば、実際にはインクルード ファイルはまったく必要ありません。関数が宣言されていないという警告が表示される場合がありますが、それらはそれらの関数のプロトタイプを自分で追加している可能性があります。たとえば、メインのソース ファイルでは のみを使用しputs、含める代わりに次の<stdio.h>ようなプロトタイプを追加できます。

int puts(const char *s);

ただし、<stdio.h>いくつかの構造体 (構造体など) も定義し、FILEいくつかの変数 (など) を宣言しstdoutます。これらのいずれかを使用する場合は、ヘッダー ファイルも必要です。

于 2012-04-23T10:09:52.027 に答える
0

@Jeffが提案したようにインクルードガードを使用するか#pragma once、各ヘッダーの上部に配置することができます。

于 2012-04-23T10:09:35.870 に答える