#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;
}
Или даже просто удалите включить вообще. Не очень хорошая практика, так как компилятор (без информации о функции) будет считать, что ее возвращаемое значение является целым числом и может вызвать другие предупреждения.