Как предотвратить множественные определения в 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() {}
Когда я пытаюсь собрать эту программу, она выдает следующие ошибки:
* путь *\test.c|1| множественное определение `_ test'| obj\Debug\main.o:*path*\test.c|1| впервые определено здесь |
Я никоим образом не могу определить критерий множественного определения (хотя я не знаю, откуда берется подчеркивание), и кажется маловероятным, что определение каким-то образом будет включено дважды. Это весь код, который есть.
Я исключил, что эта ошибка вызвана конфликтом имен с другими функциями или файлами, называемыми test или test.c. Обратите внимание, что множественное и первое определение находятся в одной строке в одном файле.
Кто-нибудь знает, что вызывает это и что я могу с этим поделать? Спасибо!
8 ответов
Вы фактически компилируете исходный код 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
Подчеркивание ставится компилятором и используется компоновщиком. Основной путь:
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().
Вы не должны включать другие исходные файлы (*.c) в файлы.c. Я думаю, что вы хотите иметь файл заголовка (.h) с ДЕКЛАРАЦИЕЙ тестовой функции и иметь его ОПРЕДЕЛЕНИЕ в отдельном файле.c.
Ошибка вызвана несколькими определениями тестовой функции (одно в test.c, а другое в main.c)
У меня была похожая проблема, и я решил ее следующим образом.
Решить следующим образом:
Объявления прототипов функций и глобальные переменные должны находиться в файле test.h, и вы не можете инициализировать глобальную переменную в заголовочном файле.
Определение функции и использование глобальной переменной в файле test.c
если вы инициализируете глобальные переменные в заголовке, он будет иметь следующую ошибку
множественное определение `_ test'| obj\Debug\main.o:путь\test.c|1| впервые определен здесь |
Просто объявления глобальных переменных в заголовочном файле не должны работать.
Надеюсь, поможет
ура
Если вы добавили test.c в свой проект Code::Blocks, определение будет отображаться дважды - один раз через #include и один раз компоновщиком. Вам нужно:
- удалите #include "test.c"
- создайте файл test.h, который содержит объявление: void test ();
включите файл test.h в main.c
Если вы используете Visual Studio, вы также можете сделать "#pragma Once" в верхней части файла заголовка, чтобы добиться того же, что и "#ifndef ..."- перенос. Некоторые другие компиляторы, вероятно, также поддерживают это..... Однако, не делайте этого:D Придерживайтесь #ifndef-wrapping для достижения кросс-компиляторной совместимости. Я просто хотел, чтобы вы знали, что вы также можете выполнить #pragma один раз, так как вы, вероятно, встретите это утверждение довольно часто при чтении кода других людей.
Удачи с этим
Включая файл реализации (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 */
Спустя много лет я обнаружил другую проблему, которая вызывает ту же ошибку, и нигде не нашел ответа. Я подумал разместить это здесь для ссылки на других людей, испытывающих ту же проблему.
Я определил функцию в файле заголовка, и она продолжала выдавать эту ошибку. ( Я знаю, что это неправильный способ, но я подумал, что быстро протестирую его таким образом.)
Решение заключалось в том, чтобы ТОЛЬКО поместить объявление в файл заголовка и определение в файл cpp.
Причина в том, что файлы заголовков не компилируются, они содержат только определения.