директива #include на этапе компоновки - PullRequest
0 голосов
/ 06 марта 2020

Заголовочный файл обычно имеет некоторую безопасную защиту, используя директивы #ifndef (или аналогичные), например:

//header.hpp
#ifndef HEADER
#define HEADER
//code
#endif  

, но у меня возникает путаница, что если мы сделаем следующее (рассмотрим два исходные коды файла):

//file1.cpp
#include "header.hpp"
//somecode  

и файл

//file2.cpp
#include "header.hpp"
//somecode  

, если бы мы сделали что-то вроде этого:

g++ file1.cpp file2.cpp -o mainfile  

мы бы получили один исполняемый файл, который получит один исполняемый файл без дублирования, так как включения проверяются во время компиляции.

Но что если мы сделаем:

g ++ - c file1. cpp -o file1.o
g ++ - c file2. cpp -o file2.o
g ++ file1.o file2.o -o mainfile.o

Что происходит на этапе компоновки? Будет ли конфликт включать? Что происходит с включениями во время компиляции? Это дублируется? Каков механизм под капотом, чтобы справиться с этим на данном этапе?

Ответы [ 4 ]

1 голос
/ 06 марта 2020

Обычно заголовочный файл содержит только объявления функций и переменных, а не их определения. Это определения, которые обрабатываются на этапе компоновки, поэтому можно многократно включать заголовок в разные исходные файлы, и компоновщик просто ничего не увидит. Если у вас есть глобальная функция или определение переменной в заголовочном файле, вы получите ошибку компоновщика.

1 голос
/ 06 марта 2020

Формальный термин здесь - «Единица перевода». Это то, что вы называете одним. cpp файлом со всеми включенными в него заголовками. Определения препроцессора не охватывают единицы перевода, и здесь у вас есть две единицы перевода. Процесс связывания - это то, что объединяет Tranlation Units, но на этом этапе препроцессор долго готов.

0 голосов
/ 06 марта 2020

#include на самом деле является директивой препроцессора, что означает, что они разрешаются до компиляции. Препроцессор обрабатывает каждую единицу перевода отдельно, и две единицы перевода не влияют друг на друга. Результаты этого шага не содержат никаких инструкций препроцессора (например, #include, #ifdef, #define, et c).

Таким образом, после предварительной обработки оба файла, file1.cpp и file2.cpp, содержат содержимое header.hpp. Затем оба компилируются в file1.o и file2.o. Пока проблем нет. Здесь приходит важность включить охранников. Компиляция не удастся, если блок перевода содержит дубликаты объявлений.

Представьте, что вы получили header1.hpp:

#include "header.hpp"

class ABC { ... };

И header2.hpp:

#include "header.hpp"

class XYZ { ... };

И какой-нибудь файл, скажем, file3.cpp будет полагаться на оба :

#include "header1.hpp"
#include "header2.hpp"

class Foo : pulbic ABC, public XYZ {};

Без включения защиты вы заканчиваете тем, что включаете дважды header.hpp и получаете все объявления дважды в модуле перевода, который не компилируется. (Мы только смотрим file3.cpp здесь). С включенной защитой header.hpp включается только один раз .

Теперь мы наконец достигли стадии связывания и вернемся к вашему первоначальному примеру. У вас есть 2 единицы компиляции, которые содержат все декалирации от header.hpp. Компоновщик не будет заботиться о дубликатах объявлений. Компоновщик потерпит неудачу только в том случае, если он найдет несколько определений символа.

#include s не "проверяются" или "игнорируются" на этапе компоновки, они просто не существует больше.

0 голосов
/ 06 марта 2020

Оба абсолютно одинаковы. Страж не go к следующему файлу. Каждый файл компилируется с новым #define - состоянием.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...