Несколько библиотек определений и только заголовков
У меня есть программа на C с несколькими файлами c и h. Я решил сделать одну часть программы "только для заголовков", поэтому переместил код с c на h. Теперь я получаю множественные проблемы с определением, и я понятия не имею, почему. например:
main.c includes utils.h
vector.c includes utils.h
Я переместил все в utils.c в utils.h (и, конечно, удалил utils.c из проекта). utils.h начинается с
#ifndef UTILS_H_
#define UTILS_H_
// and end with:
#endif
Чтобы убедиться, что моя охрана была уникальной, я попытался изменить ее (например: UTILS718171_H_), но она не работает.
Тем не менее, компилятор жалуется:
/tmp/ccOE6i1l.o: In function `compare_int':
ivector.c:(.text+0x0): multiple definition of `compare_int'
/tmp/ccwjCVGi.o:main.c:(.text+0x660): first defined here
/tmp/ccOE6i1l.o: In function `compare_int2':
ivector.c:(.text+0x20): multiple definition of `compare_int2'
/tmp/ccwjCVGi.o:main.c:(.text+0x6e0): first defined here
/tmp/ccOE6i1l.o: In function `matrix_alloc':
ivector.c:(.text+0x40): multiple definition of `matrix_alloc'
/tmp/ccwjCVGi.o:main.c:(.text+0x0): first defined here
...
Проблема может выглядеть примерно так: все файлы c компилируются и получают свою собственную версию кода, а затем при связывании это вызывает проблему, но я, честно говоря, понятия не имею, как решить эту проблему.
4 ответа
Если вы определяете свои переменные в своем заголовочном файле и включаете заголовок в несколько файлов c, вы неизбежно получите ошибку множественных определений, потому что нарушаете правило одного определения (ODR), в котором говорится, что в одной единице перевода должно быть только одно определение. (заголовочные файлы + исходный файл).
Решение:
Вы должны определить объекты, которые вы получаете несколько ошибок определения только один раз.
Для функций:
Объявите прототипы функций в заголовочном файле (который вы включаете в другие исходные файлы) и определите функцию в одном и только одном исходном файле.
Для глобальных переменных:
Вы объявляете переменную extern в заголовочном файле (который вы включаете в другие исходные файлы), а затем определяете переменную в одном и только одном исходном файле.
Вы упускаете точку конструкции #ifndef _FOO_H / #define _FOO_H / #endif. Это защищает только от нескольких включений в одном файле. Например, они защищают от этого:
foo.h:
#ifndef _FOO_H
#define _FOO_H
/* some C stuff here */
#endif /* _FOO_H */
foo.c:
#include <foo.h>
#include <bar.h>
...
bar.h:
#include <foo.h>
...
обратите внимание, что foo.c и bar.h оба включают foo.h; здесь #ifdef _FOO_H / #define _FOO_H / #endif защищает от этого двойного включения в foo.c (foo.h, включенный в bar.h, не переопределяет вещи)
Теперь следующая часть.
Зачем вам перемещать реализацию функции из utils.c в utils.h? Кроме того, почему вы решили сделать его "только для заголовков"?
Вы можете, в зависимости от того, поддерживает ли ваш компилятор static inline
функции делают это; но даже тогда это НЕ рекомендуется, так как более чем вероятно, это сделает вашу программу излишне раздутой, потому что скорее всего ваши утилиты довольно сложны и не могут быть встроены в любом случае.
Так что измените его обратно на utils.c и utils.h construct, который у вас был до этого, который, я полагаю, БЫЛ работающим и наслаждающимся программным обеспечением.
Если вы не заинтересованы в utils.h
функции, которые нужно "скопировать" в каждое место, где он используется, просто используйте static
функции в шапке. (static inline
в C99)
Вы нарушаете Правило Одного Определения. Каждая функция должна быть определена ровно один раз, а вы в конечном итоге определяете ее в каждой единице перевода.
Вы просто не можете сделать что-то вроде этого:
// header.h
void foo() { }
// file1.c
#include "header.h"
// file2.c
#include "header.h"
Единственное реальное решение - объявить void foo();
в заголовке и определите его только один раз (обычно в выделенном foo.c
). То же самое касается глобальных переменных, которые должны быть объявлены как extern
в заголовке и определяется в исходном файле.
Включать охранников тут ни при чем. Они служат только для предотвращения рекурсивного самостоятельного включения или избыточного множественного включения в одном блоке перевода.