Как предотвратить множественные определения в C? - PullRequest
35 голосов
/ 23 марта 2009

Я новичок в 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|multiple definition of `_ test'|
obj\Debug\main.o:*path*\test.c|1|first defined here|

Нет способа, которым я бы многократно определял тест (хотя я не знаю, откуда берется подчеркивание), и кажется маловероятным, что это определение каким-то образом включено дважды. Это весь код, который есть.

Я исключил, что эта ошибка вызвана конфликтом имен с другими функциями или файлами, называемыми test или test.c. Обратите внимание, что множественное и первое определение находятся в одной строке в одном файле.

Кто-нибудь знает, что вызывает это и что я могу с этим поделать? Спасибо!

Ответы [ 7 ]

81 голосов
/ 23 марта 2009

Вы фактически компилируете исходный код test.c дважды:

  • Первый раз при самой компиляции test.c,
  • Второй раз при компиляции main.c, который включает все источники test.c.

Что вам нужно в вашем main.c для использования функции test(), это простое объявление, а не его определение. Это достигается путем включения заголовочного файла 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
26 голосов
/ 23 марта 2009

Подчеркивание ставится там компилятором и используется компоновщиком. Основной путь:

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

Итак, ваша основная программа должна включать заголовочный файл для тестового модуля, который должен состоять только из объявлений, таких как прототип функции:

void test(void);

Это позволяет компилятору знать, что он существует во время компиляции main.c, но фактический код находится в test.c, затем test.o.

Эта фаза связывания объединяет два модуля.

Включив test.c в main.c, вы определяете функцию test () в main.o. Предположительно, вы затем связываете main.o и test.o, оба из которых содержат функцию test ().

10 голосов
/ 23 марта 2009

Вы не должны включать другие исходные файлы (* .c) в .c файлы. Я думаю, что вы хотите иметь файл заголовка ( .h) с DECLARATION тестовой функции и иметь его DEFINITION в отдельном файле .c.

Ошибка вызвана несколькими определениями тестовой функции (одно в test.c, а другое в main.c)

6 голосов
/ 25 сентября 2012

У меня была похожая проблема, и я решил ее следующим образом.

Решить следующим образом:

Объявления прототипов функций и глобальные переменные должны находиться в файле test.h, и вы не можете инициализировать глобальную переменную в заголовочном файле.

Определение функции и использование глобальной переменной в файле test.c

если вы инициализируете глобальные переменные в заголовке, у него будет следующая ошибка

множественное определение `_ test '| obj \ Debug \ main.o: путь \ test.c | 1 | впервые определен здесь |

Просто объявления глобальных переменных в заголовочном файле не должны работать при инициализации.

Надеюсь, это поможет

Приветствия

4 голосов
/ 23 марта 2009

Если вы используете Visual Studio, вы также можете сделать «#pragma Once» в верхней части файла заголовка, чтобы добиться того же, что и «#ifndef ...» - перенос Некоторые другие компиляторы, вероятно, также поддерживают это .. .. Однако, не делайте этого: D Придерживайтесь # ifndef-wrapping для достижения кросс-компиляторной совместимости. Я просто хотел, чтобы вы знали, что вы также можете выполнить #pragma один раз, так как вы, вероятно, встретите это утверждение довольно часто при чтении кода других людей.

Удачи с этим

4 голосов
/ 23 марта 2009

Если вы добавили test.c в свой проект Code :: Blocks, определение будет видно дважды - один раз через #include и один раз компоновщиком. Вам необходимо:

  • удалить #include "test.c"
  • создайте файл test.h, который содержит объявление: void test ();
  • включает файл test.h в main.c

4 голосов
/ 23 марта 2009

Включение файла реализации (test.c) приводит к его добавлению в файл main.c и выполнению его там же, а затем снова отдельно. Итак, функция test имеет два определения - одно в объектном коде main.c и одно в test.c, что дает вам нарушение ODR. Вам необходимо создать заголовочный файл, содержащий объявление test и включить его в main.c:

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