Заголовок / Включить защитные устройства не работают?

#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 -то все и происходит