Как происходит включение заголовочного файла? - PullRequest
0 голосов
/ 01 апреля 2009

У меня есть простой код C с файлами * .c и * .h в рабочей области.
У меня есть заголовочный файл 1.h, объявляющий некоторую структуру как

struct my1
{ 
int a;
..
..
}my_t;

Но когда я пытаюсь объявить переменную типа struct my1 в другом заголовочном файле 2.h следующим образом: -

struct my1 variable1;

В этой точке декларации выдается ошибка.

Похоже, что my1 здесь не определено в файле 2.h.

В файле 1.h мне нужно включить 2.h, поэтому в файле 2.h я не могу включить 1.h из-за боязни рекурсивного включения.

Мой вопрос: -

  1. Что я должен объявить для устранения ошибки компиляции в этом случае?

    Все это заставило меня задуматься над дальнейшими вопросами о включениях заголовочных файлов.

  2. Как файлы заголовков включены, в каком порядке, какой файл заголовка первым, а какой?

  3. Приведет ли рекурсивное включение заголовочных файлов к ошибкам одного файла, в том числе другого, и другого, в том числе первого?

Не удалось опубликовать фактические фрагменты кода по какой-либо причине безопасности, поэтому извините, если вопрос вызывает проблемы с читабельностью.

Ответы [ 7 ]

4 голосов
/ 01 апреля 2009

Вы должны начать с установки блокировки включения во все ваши файлы .h (это называется include guard ):

#ifndef ONE_H
#define ONE_H

//rest of header

#endif //ONE_H

Таким образом, вы можете включить его несколько раз.

Второе:

typedef struct my1 { int a; .. .. }my_t;

Вам нужен typedef в C (не в C ++)

Заголовки включены в порядке включения.

Если вы компилируете файл abc.c, который начинается с:

#include "a.h"
#include "b.h"

тогда сначала будет включен a.h, затем b.h.

Вы можете думать об этом, как будто вы вставляете код в файл. Включено в этот момент.

2 голосов
/ 01 апреля 2009

Это как люди говорили раньше.

Я просто хочу добавить, что иногда даже #ifdef вам не поможет.

//file1.h
#ifndef F1
#define F1

#include "file2.h"

struct file1st {
  struct file2st *ptr;
};

#endif

//file2.h

#ifndef F2
#define F2

#include "file1.h"

struct file2st {
   struct file1st *ptr;
};

#endif

//main.c

#include "file1.h"
#include "file2.h"

/*
This will give you an error of **struct file1st not defined**
Let's see why: 

1) file1.h is included
2) file1.h includes file2.h before it declares anything
3) the definition of struct file2st occurs and it uses struct file1st which isn't declared yet
*/

int main(int argc, char* argv[]){
  struct file1st st1;
  struct file2st st2;

  return 0;
}

Способ решить эту проблему:

//file1.h
    #ifndef F1
    #define F1

    struct file2st;//just declare, it will be defined later. 

    struct file1st {
      struct file2st *ptr; // ok, compiler KNOWS the size of struct file2st*(pointer)
      struct file2st file2Var;// NOT ok, compiler doesn't know sizeof(struct file2st)
    };

    #endif

    //file2.h

    #ifndef F2
    #define F2

    #include "file1.h"

    struct file2st {
       struct file1st *ptr;
    };

    #endif
1 голос
/ 01 апреля 2009

Каждый заголовочный файл включен в каждую единицу перевода (исходный файл), в которой есть директива включения для него. Это предназначено и будет происходить даже с защитой от включения - каждый модуль перевода, который использует вашу структуру, должен знать, как определяется эта структура, чтобы ее можно было разместить в памяти одинаково для всех модулей перевода вашего приложения. Защитные устройства для включения просто не позволяют включать его несколько раз в одну единицу перевода . Включаемые файлы будут включены в том порядке, в котором вы их включили в этот модуль перевода (и они будут включены рекурсивно, если включаемые файлы включают другие файлы ... как уже сказали другие). Порядок компиляции блоков перевода зависит от вас (или вашей IDE), которые вы указываете компилятору. Однако не должно иметь значения, каков этот порядок, поскольку каждый модуль перевода полностью независим, пока не дойдет до фазы связывания процесса сборки.

1 голос
/ 01 апреля 2009

Я второй советник охраны.

Я религиозно использую следующий шаблон заголовка:

#ifndef HELLOWORLD_H_
#define HELLOWORLD_H_

// Header stuff here.

#endif // HELLOWORLD_H_

Когда компилятор видит #include, он просто заменяет эту строку содержимым файла заголовка (за исключением любых обработанных директив в заголовке). Таким образом, это означает, что вы можете включать файл в любое количество мест без риска рекурсивных включений.

1 голос
/ 01 апреля 2009

Заголовочные файлы включены в порядке include директив. Как только компилятор видит директиву include , он открывает файл для включения и просто вставляет все его содержимое во включающий файл.

Если во включенном файле есть директивы include , то же самое делается для них. Этот процесс продолжается до тех пор, пока не будут обработаны все директивы include .

Только после этого начинается компиляция.

Вот почему, если какой-либо файл включен более одного раза (например, A включает B и C; например, B и C включают D), вы часто будете видеть, что компилятор жалуется на переопределения. Чтобы решить эту проблему, добавьте блокировки включения (иначе включают охрану ) - директивы ifdef .

//file Header1
#ifndef Header1Guard
   #define Header1Guard
// all the header text here
#endif
0 голосов
/ 01 апреля 2009
// Header1.h
typedef struct tagHeader1
{
} Header1;

// Header2.h

struct Header1;

// Header2.c

#include "Header1.h"

Примечание: это работает только для указателей (и в c ++, ссылки). Если у вас есть ссылка на законченный объект, компилятор должен будет знать об этом.

0 голосов
/ 01 апреля 2009

Прошло много времени с тех пор, как я работал с C, но я думаю, что вы хотите сделать, это далее определить my1.

В 2.h, попробуйте поставить это около вершины:

struct my1;

Извините, я не могу ответить на два других ваших вопроса.

...