Путаница с директивами препроцессора

У меня три файла

Файл "grandparent.h"

#ifndef GRANDPARENT_H
#define GRANDPARENT_H

struct foo {
    int member;
};

#endif /* GRANDPARENT_H */

Файл "parent.h"

#include "grandparent.h"

Файл "child.c"

 #include "grandparent.h"
 #include "parent.h"

Вики говорит

Здесь первое включение "grandparent.h" приводит к определению макроса GRANDPARENT_H. Затем, когда "child.c" включает в себя "grandparent.h" во второй раз, тест #ifndef возвращает false, и препроцессор переходит к #endif, тем самым избегая второго определения struct foo. Программа компилируется правильно.

q1. "первое включение" grandparent.h "приводит к определению макроса GRANDPARENT_H", так что я понимаю, что это в основном определение макроса с именем GRANDPARENT_H но я не понимаю, как содержимое этого макроса (например, GRANDPARENT_H) будет включено в child.c.

Мы просто определяем макрос GRANDPARENT_H, т.е.

#define GRANDPARENT_H

struct foo {
    int member;
};

но как будет его содержание т.е.

struct foo {
    int member;
};

быть включенным в child.c

1 ответ

Решение

Если вы "расширить" child.c вручную, пока нет #include оставил:

//grandparent.h

#ifndef GRANDPARENT_H    // <- not defined at this point
#define GRANDPARENT_H    // <- now it's defined

struct foo {
    int member;
};

#endif /* GRANDPARENT_H */
//parent.h

//#include "grandparent.h" resolves into
//grandparent.h

#ifndef GRANDPARENT_H   // <- already defined, skip until matching #endif
#define GRANDPARENT_H   // <- not executed by preprocessor

struct foo {            // <- not provided to the compiler
    int member;
};

#endif /* GRANDPARENT_H */

Теперь прочитайте это последовательно. Первая строка проверяет, является ли макрос GRANDPARENT_H определено. Очевидно, что это не так, поскольку это первая инструкция кода.

Вторая строка определяет GRANDPARENT_H макро. Это пусто, но это не важно, важно то, что оно определено.

Затем код определяет вашу структуру...

Когда препроцессор сталкивается со вторым #ifdef GRANDPARENT_Hмакрос уже определен, поэтому он пропускает все содержимое файла, и вы ничего не получите foo redefined ошибка.

Что подтверждается с помощью -E возможность увидеть предварительно обработанный child.c файл:

$ gcc -E child.c
# 1 "child.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "child.c"
# 1 "grandparent.h" 1


struct foo {
    int member;
};
# 2 "child.c" 2
# 1 "parent.h" 1
# 2 "child.c" 2

Как видите, структура определяется только один раз.

Обратите внимание, что большинство компиляторов теперь поддерживают более простой способ сделать это: просто вставьте

#pragma once

в начале вашего файла. Как это:

#pragma once

struct foo {
    int member;
};

вот и все!

Другие вопросы по тегам