Как предотвратить множественные определения в 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.

Причина в том, что файлы заголовков не компилируются, они содержат только определения.

Другие вопросы по тегам