22

いくつかの c および h ファイルを含む C プログラムがあります。プログラムの一部を「ヘッダーのみ」にすることにしたので、コードを c から h に移動しました。現在、倍数定義の問題が発生していますが、その理由がわかりません。例えば:

main.c includes utils.h
vector.c includes utils.h

utils.c のすべてを utils.h に移動しました (もちろん、プロジェクトから utils.c を削除しました)。utils.h は次で始まります

#ifndef UTILS_H_
#define UTILS_H_

// and end with:
#endif

ガードが一意であることを確認するために、変更してみました (例: UTILS718171_H_) が、機能しません。

それでも、コンパイラは不平を言います:

/tmp/ccOE6i1l.o: In function `compare_int':
ivector.c:(.text+0x0): multiple definition of `compare_int'
/tmp/ccwjCVGi.o:main.c:(.text+0x660): first defined here
/tmp/ccOE6i1l.o: In function `compare_int2':
ivector.c:(.text+0x20): multiple definition of `compare_int2'
/tmp/ccwjCVGi.o:main.c:(.text+0x6e0): first defined here
/tmp/ccOE6i1l.o: In function `matrix_alloc':
ivector.c:(.text+0x40): multiple definition of `matrix_alloc'
/tmp/ccwjCVGi.o:main.c:(.text+0x0): first defined here
...

問題は次のようなものかもしれません.すべてのcファイルがコンパイルされ、独自のバージョンのコードが取得され、リンク時に問題が発生しますが、正直なところ、この問題を解決する方法がわかりません.

4

4 に答える 4

32

ヘッダー ファイル内で変数を定義し、ヘッダーを複数の c ファイルに含めると、 1 つの翻訳単位に 1 つの定義のみが存在する必要があるというOne definition rule(ODR)に違反するため、複数の定義エラーが発生する可能性があります。 (ヘッダー ファイル + ソース ファイル)。

解決策は次
のとおり です。複数の定義エラーが発生するエンティティは、1 回だけ定義する必要があります。
関数の場合:
ヘッダー ファイル (他のソース ファイルに含める) で関数プロトタイプを宣言し、1 つのソース ファイルで関数を定義します。
グローバル変数の場合:
ヘッダー ファイル (他のソース ファイルに含める) で変数 extern を宣言し、1 つのソース ファイルで変数を定義します。

于 2011-11-20T13:52:59.910 に答える
11

#ifndef _FOO_H / #define _FOO_H / #endif コンストラクトのポイントがありません。これは、1 つのファイルに複数のインクルードが含まれるのを防ぐだけです。たとえば、次のように保護します。

foo.h:

  #ifndef _FOO_H 
  #define _FOO_H

  /* some C stuff here */

  #endif /* _FOO_H */

foo.c:

   #include <foo.h>
   #include <bar.h>
   ...

bar.h:

   #include <foo.h>
   ...

foo.c と bar.h の両方に foo.h が含まれていることに注意してください。ここで、 #ifdef _FOO_H / #define _FOO_H / #endif は、foo.c への二重の包含から保護します (bar.h に含まれる foo.h は再定義されません)。

次の部分です。

関数の実装を utils.c から utils.h に移動したのはなぜですか? また、なぜ「ヘッダーのみ」にすることにしたのですか?

static inlineコンパイラが関数をサポートしているかどうかに応じて、これを行うことができます。ただし、それでもお勧めしません。ユーティリティ関数が非常に複雑で、とにかくインライン化できない可能性があるため、プログラムが不必要に肥大化する可能性が高いためです。

したがって、以前に使用していた utils.c および utils.h コンストラクトに戻してください。これは、動作していて、ソフトウェアを楽しんでいたと思われます。

于 2011-11-20T14:02:31.333 に答える
5

1 つの定義ルールに違反しています。各関数は、翻訳単位ごとに定義することになりますが、正確に1 回定義する必要があります。

次のようなことはできません。

// header.h
void foo() { }

// file1.c
#include "header.h"

// file2.c
#include "header.h"

唯一の本当の解決策は、ヘッダーで宣言し、それを一度だけ 定義することです (通常は専用の で)。同じことがグローバル変数にも当てはまります。グローバル変数はヘッダーで宣言し、ソース ファイルで定義する必要があります。void foo();foo.cextern

インクルードガードはこれとは何の関係もありません。それらは、再帰的な自己包含または1 つの翻訳単位内での冗長な複数包含を防ぐためにのみ機能します。

于 2011-11-20T13:54:29.037 に答える
4

関数が使用される各場所に「コピー」されることを意図している場合は、ヘッダーの関数をutils.h使用するだけです。staticstatic inlineC99で)

于 2011-11-20T14:09:00.503 に答える