web-dev-qa-db-ja.com

Cで複数の定義を防ぐ方法は?

私はC初心者であり、Code :: Blocksを使用してコンソールアプリケーションを作成しようとしていました。これは(簡略化された)コードです:main.c:

#include <stdio.h>
#include <stdlib.h>
#include "test.c" // include not necessary for error in Code::Blocks

int main()
{
    //t = test(); // calling of method also not necessary
    return 0;
}

test.c:

void test() {}

このプログラムをビルドしようとすると、次のエラーが発生します。

 * path *\test.c | 1 |「_ test」の複数の定義| 
 obj\Debug\main.o:* path *\test.c | 1 |最初にここで定義| 

テストを多重定義する方法はありませんが(アンダースコアがどこから来ているのかはわかりませんが)、定義が何らかの形で2回含まれていることはほとんどありません。これがすべてのコードです。

このエラーは、testまたはtest.cと呼ばれる他の関数またはファイルとの名前の競合が原因であるとは考えていません。複数の定義と最初の定義が同じファイルの同じ行にあることに注意してください。

誰がこれを引き起こしているのか、私はそれについて何ができるのか知っていますか?ありがとう!

38
Jordi

実際に_test.c_のソースコードを2回コンパイルします。

  • _test.c_自体を初めてコンパイルするとき、
  • すべての_main.c_ソースを含む_test.c_をコンパイルする2回目。

test()関数を使用するために_main.c_で必要なのは、その定義ではなく単純な宣言です。これは、次のようなものを含む_test.h_ヘッダーファイルを含めることで実現されます。

_void test(void);
_

これは、入力パラメーターと戻り値の型を持つ関数が存在することをコンパイラーに通知します。この関数が行うこと(_{_および_}_内のすべて)は_test.c_ファイルに残されます。

Main.cで、_#include "test.c"_を_#include "test.h"_に置き換えます。

最後のポイント:プログラムがより複雑になると、ヘッダーファイルが数回含まれる場合があります。これを防ぐため、ヘッダーソースは次のような特定のマクロ定義で囲まれている場合があります。

_#ifndef TEST_H_INCLUDED
#define TEST_H_INCLUDED

void test(void);

#endif
_
84
mouviciel

アンダースコアはコンパイラーによってそこに置かれ、リンカーによって使用されます。基本的なパスは次のとおりです。

main.c
test.h ---> [compiler] ---> main.o --+
                                     |
test.c ---> [compiler] ---> test.o --+--> [linker] ---> main.exe

したがって、メインプログラムには、関数プロトタイプなどの宣言のみで構成されるテストモジュールのヘッダーファイルを含める必要があります。

void test(void);

これにより、コンパイラはmain.cがコンパイルされているときに存在することを認識できますが、実際のコードはtest.cにあり、次にtest.oにあります。

2つのモジュールを結合するのはリンクフェーズです。

Test.cをmain.cに含めることにより、main.oでtest()関数を定義しています。おそらく、あなたはmain.oとtest.oをリンクしているでしょう。どちらにも関数test()が含まれています。

26
paxdiablo

。cファイルに他のソースファイル(* .c)を含めるべきではありません。テスト機能の宣言を含むヘッダー(。h)ファイルを作成し、その定義を別の.cファイル。

このエラーは、テスト関数の複数の定義(test.cの1つとmain.cの他の定義)が原因で発生します

10
Kasprzol

私は同様の問題があり、次の方法で解決しました。

次のように解決します。

関数プロトタイプ宣言とグローバル変数はtest.hファイルにある必要があり、ヘッダーファイルでグローバル変数を初期化することはできません。

Test.cファイルの関数定義とグローバル変数の使用

ヘッダーのグローバル変数を初期化すると、次のエラーが発生します

`_ test 'の複数の定義| obj\Debug\main.o:パス\test.c | 1 |ここで最初に定義|

ヘッダーファイルでグローバル変数を宣言するだけで、初期化は機能しません。

それが役に立てば幸い

乾杯

6
Murad

Visual Studioを使用している場合は、ヘッダーファイルの上部で「#pragma once」を実行して、「#ifndef ...」と同じことを実現することもできます。他のいくつかのコンパイラもおそらく同様にサポートしています....しかし、これをしないでください:Dクロスコンパイラ互換性を実現するために#ifndef-wrappingに固執します。他の人のコードを読んでいるときにこの文に出会う可能性が高いため、#pragmaを1回実行することもできます。

頑張って

4
cwap

実装ファイル(test.c)を含めると、main.cの先頭に追加され、そこでコンパイルされてから個別にコンパイルされます。そのため、関数testには2つの定義があります。1つはmain.cのオブジェクトコードに、もう1つはtest.cのオブジェクトコードにあり、ODR違反になります。 testの宣言を含むヘッダーファイルを作成し、main.cに含める必要があります。

/* test.h */
#ifndef TEST_H
#define TEST_H
void test(); /* declaration */
#endif /* TEST_H */
4
dirkgently

Test.cをCode :: Blocksプロジェクトに追加した場合、定義は2回表示されます-#includeを介して1回、リンカーを介して1回。必要がある:

  • #include "test.c"を削除します
  • 宣言を含むファイルtest.hを作成します。void test();
  • main.cにファイルtest.hを含めます

4
anon