#c #header #include-guards
#c #заголовок #включить охрану
Вопрос:
По какой-то причине я получаю несколько объявлений содержимого в моем заголовочном файле, даже если я использую защиту заголовка. Мой пример кода приведен ниже:
main.c:
#include "thing.h"
int main(){
printf("%d", increment());
return 0;
}
thing.c:
#include "thing.h"
int increment(){
return something ;
}
thing.h:
#ifndef THING_H_
#define THING_H_
#include <stdio.h>
int something = 0;
int increment();
#endif
Когда я пытаюсь скомпилировать это, GCC сообщает, что у меня есть несколько определений переменной something. ifndef должен убедиться, что этого не произойдет, поэтому я не понимаю, почему это так.
Комментарии:
1. Ошибка компилятора «несколько объявлений » не совпадает с ошибкой компоновщика «несколько определений «. Вы упомянули оба в своем вопросе (хотя на самом деле единственной проблемой является последнее); понимание разницы является ключом к пониманию того, что происходит не так. Защита заголовка предотвращает несколько объявлений , а не несколько определений .
2. @Clifford извините, я должен был упомянуть, что получаю ошибку компоновщика.
3. Кроме того, в C
int increment();
не является прототипом, но объявляет функцию с неопределенным количеством параметров. Используйтеint increment(void);
для этого.
Ответ №1:
Защита включения работает правильно и не является источником проблемы.
Что происходит, так это то, что каждый модуль компиляции, который включает thing.h
, получает свой собственный int something = 0
, поэтому компоновщик жалуется на несколько определений.
Вот как вы это исправляете:
thing.c:
#include "thing.h"
int something = 0;
int increment(){
return something ;
}
thing.h:
#ifndef THING_H_
#define THING_H_
#include <stdio.h>
extern int something;
int increment();
#endif
Таким образом, only thing.c
будет иметь экземпляр something
, и main.c
будет ссылаться на него.
Комментарии:
1. Вы могли бы также добавить, что даже если не будет множественных ошибок определения, каждый TU будет иметь свою собственную копию переменной, и это не достигнет цели, которая заключается в совместном использовании одной и той же переменной во всех исходных файлах.
Ответ №2:
У вас есть одно определение в каждой единице перевода (одно в main.c
и одно в thing.c
). Защита заголовка предотвращает включение заголовка более одного раза в одну единицу перевода.
Вам нужно объявить something
в заголовочном файле и только определить его thing.c
, как и функцию:
thing.c:
#include "thing.h"
int something = 0;
int increment(void)
{
return something ;
}
thing.h:
#ifndef THING_H_
#define THING_H_
#include <stdio.h>
extern int something;
int increment(void);
#endif
Ответ №3:
Защита заголовка остановит компиляцию файла более одного раза в одном и том же блоке компиляции (файле). Вы включаете его в main.c и thing.c, поэтому он будет скомпилирован один раз в каждом, что приведет к something
объявлению переменной один раз в каждом блоке или всего два раза.
Ответ №4:
старайтесь избегать определения переменных глобально. вместо этого используйте функции, такие как increment(), чтобы изменить и прочитать его значение. таким образом, вы можете сохранить переменную статической в файле thing.c, и вы точно знаете, что только функции из этого файла будут изменять значение.
Комментарии:
1. Ах, так вот как вы избегаете глобальных переменных в C. Спасибо за информацию.
Ответ №5:
Переменная something
должна быть определена в .c
файле, а не в заголовочном файле.
В файлах заголовков должны быть только структуры, макросы и объявления типов для переменных и прототипов функций. В вашем примере вы можете объявить тип something
as extern int something
в файле заголовка. Но определение самой переменной должно быть в .c
файле.
С тем, что вы сделали, переменная something
будет определена в каждом .c
файле, который включает thing.h
, и вы получите сообщение об ошибке «что-то определенное несколько раз», когда GCC пытается связать все вместе.
Комментарии:
1. Ваше первое утверждение неверно. Переменная должна быть объявлена с ключевым словом extern в заголовочном файле и определена в одном и единственном исходном файле.
Ответ №6:
то, что ifndef
охраняет .h
, включено в a .c
более одного раза. Например
thing. h
#ifndef
#define
int something = 0;
#endif
thing2.h
#include "thing.h"
main.c
#include "thing.h"
#include "thing2.h"
int main()
{
printf("%d", something);
return 0;
}
если я оставлю ifndef
это, GCC будет жаловаться
In file included from thing2.h:1:0,
from main.c:2:
thing.h:3:5: error: redefinition of ‘something’
thing.h:3:5: note: previous definition of ‘something’ was here
Комментарии:
1. Хотя ваше объяснение верно, проблема OP не в этом, защита включения определенно установлена .
2. Я могу согласиться с @manuzhang, учитывая то, что я здесь прочитал. Я оставлю их для формы, но я не планирую включать заголовок несколько раз в одну единицу компиляции. 🙂
3. ну, с более крупными проектами вы должны это делать, и вот тут
ifndef
-то все и происходит