Дубликат символа в C

#c #function #duplicate-symbol

#c #функция #дубликат-символ

Вопрос:

У меня есть два исходных файла:

Исходный файл 1 (assembler.c):

 #include "parser.c"
int main() {
    parse_file("test.txt");
    return 0;
}
  

Исходный файл 2 (parser.c):

 void parse_file(char *config_file);
void parse_file(char *src_file) {
    // Function here
}
  

По какой-то причине при компиляции он выдает следующую ошибку:
duplicate symbol _parse_file in ./parser.o and ./assembler.o for architecture x86_64

Почему он выдает мне дубликат символа для parse_file? Я просто вызываю функцию здесь … Нет?

Комментарии:

1. В C нет классов … просто придирка к вашей формулировке.

2. Итак, пока вы «придираетесь», почему бы вам не предложить термины «модули», «исходные файлы» или «единицы перевода»?

Ответ №1:

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

В вашем случае происходит то, что у вас есть две копии parse_file функции, по одной в каждой единице перевода. Когда parser.c он компилируется в объектный файл, он имеет свою собственную parse_file функцию, а assembler.c также имеет свою собственную.

Это компоновщик, который жалуется (а не компилятор), когда ему в качестве входных данных предоставляются два объектных файла, каждый из которых содержит свое собственное определение parse_file .

Вы должны реструктурировать свой проект следующим образом:

parser.h

 void parse_file(char *);
  

parser.c

 void parse_file(char *src_file) {
    // Function here
}
  

ассемблер.c

 /* note that the header file is included here */
#include "parser.h"

int main (void) {
    parse_file("test.txt");
    return 0;
}
  

Ответ №2:

Вы включаете файл parser.c, что означает, что весь код, который находится в этом файле, будет «скопирован» в файл assembler.c. Это означает, что все содержимое parser.c будет скомпилировано, когда компилятор будет компилировать parser.c, а затем оно будет скомпилировано снова, когда компилятор будет компилировать assembler.c

Для этого и нужны заголовки.
В заголовок вы можете поместить только объявления, чтобы вы могли включить его, не создавая те же символы снова в другой единице перевода.

таким образом, вы можете просто создать parser.h, содержащий только объявление функции:

 void parse_file(char *config_file);
  

затем в вашем assembler.c вы включаете только заголовок:

 #include "parser.h" //include the header, not the implementation
int main() {
    parse_file("test.txt");
    return 0;
}
  

Комментарии:

1. Вам определенно нужен «parser.h» для вашего прототипа функции и любых определений типов. Вам также нужен «защитный», например «#ifndef PARSER_H #define PARSER_H … #endif /* PARSER_H */

2. PS: филипе (и несколько мгновений спустя, Благовест Буюкли) дают вам «решение». Дэвид Хеффернан объясняет, ПОЧЕМУ решение необходимо. PPS: не забывайте о защите «#ifndef» ;)! Возможно, вы также захотите добавить «extern «C» {}».

3. Да, да, я знаю об этом. Я совершенно забыл об этом, потому что я не делал C почти год. Я знаю, что я должен делать. Спасибо!

Ответ №3:

Вы включаете файл .c, который содержит определение функции parse_file . Таким образом, он определяется дважды, по одному разу в каждой единице перевода, что недопустимо.

Ответ №4:

Как указано в других ответах, включение источника означает, что файл будет скопирован в parser.c и будет определен там также в исходном месте (assembler.c). Чтобы решить эту проблему, либо создайте файл заголовка с вашим прототипом:

parser.h

 void parse_file(char *config_file);
  

И включить этот файл:

ассемблер.c

 #include "parser.h"
int main() {
    parse_file("test.txt");
    return 0;
}
  

Или удалите include и предоставьте ключ к функции:

 int main() {
    void parse_file(char *);
    parse_file("test.txt");
    return 0;
}
  

Или даже просто удалите включить вообще. Не очень хорошая практика, так как компилятор (без информации о функции) будет считать, что ее возвращаемое значение является целым числом и может вызвать другие предупреждения.