Зачем включать охранников?
Я изучаю C.
Включить защиту, как определено здесь, используются для предотвращения загрузки одного и того же кода дважды при компиляции.
Почему мой компилятор (GCC) не может обнаружить, что он дважды загружает один и тот же код и имеет разумное поведение по умолчанию?
Ответы
Ответ 1
Просто потому, что вы, возможно, захотели, чтобы компилятор дважды загружал этот файл.
Помните, что #include
просто загружает файл и помещает его содержимое вместо директивы. Этот файл может быть заголовочным файлом, но может быть полезным и часто используемым фрагментом исходного кода.
Большинство современных компиляторов реагируют на #pragma once
, делая именно то, что вы хотите. Помните, однако, что это расширение компилятора, не включенное в спецификацию языка, и, как правило, рекомендуется придерживаться включения защитников - вы будете уверены, что он работает на каждом компиляторе и при любых обстоятельствах.
Ответ 2
Почему мой компилятор (GCC) не может обнаружить, что он дважды загружает один и тот же код
Он может (или, педантично, препроцессор, который имеет дело с включением заголовка). Вместо использования включенных охранников вы можете использовать нестандартное, но широко поддерживаемое расширение
#pragma once
чтобы указать, что этот заголовок должен быть включен только один раз.
и имеют разумное поведение по умолчанию?
Язык не определяет это поведение по умолчанию, в основном потому, что язык относится ко временам, когда отслеживание включенных заголовков может быть чрезмерно дорогостоящим, а отчасти потому, что иногда вы хотите включать заголовок более одного раза. Например, стандартный <assert.h>
заголовок может быть повторно включен с или без NDEBUG
, заданный для изменения поведения макроса assert
.
Ответ 3
Потому что есть причудливые крайние случаи, когда полезно снова включать файл.
Продуманный уродливый пример: предположим, что у вас есть #include
файл mymin.h
, как это:
// mymin.h : ugly "pseudo-template" hack
MINTYPE min(MINTYPE a, MINTYPE b)
{
return (a < b) ? a : b;
}
Затем вы можете сделать что-то вроде этого:
#define MINTYPE int
#include "mymin.h"
#define MINTYPE double
#include "mymin.h"
Теперь у вас есть две перегрузки min
для разных типов и хороший кандидат для http://thedailywtf.com/. Кому нужны шаблоны?;-)
Обратите внимание, что многие современные препроцессоры поддерживают #pragma once
, что гораздо более приятным способом достижения такого же эффекта, как и защита. Однако, к сожалению, он не является стандартным.
Ответ 4
Почему мой компилятор (GCC) не может обнаружить, что он дважды загружает один и тот же код и имеет разумное поведение по умолчанию?
Потому что это не компилятор, делающий обработку include. Это делается препроцессором, который по существу является механизмом преобразования текста. И для механизма преобразования текста это может иметь смысл, если одно и то же приложение появляется несколько раз при обработке фрагмента текста.
Остановите это на мгновение: компилятор не обрабатывает #include
s. Это делает невозможным принятие разумных решений о переопределении символов компилятором.
Другие языки реализуют модули как часть языка, и на этих языках вещи не обрабатываются как текстовая подстановка, и у компилятора действительно есть знания о семантике импорта.
Ответ 5
Даже если компилятор решает это сделать, ему нужно отслеживать огромное количество файлов и много раз (как комментируется itwasntpete), компилятор не имеет возможности различать фактический код и заголовочный файл.
Ответ 6
Включить защитные меры для защиты от переопределения символов и включения одних и тех же файлов несколько раз.
Компилятор нуждается в этом механизме, потому что по понятным причинам он не включает механизм анализа и принятия решения о том, какую версию кода следует учитывать. Подумайте, что произойдет, если одна и та же сигнатура функции в двух разных заголовочных файлах только тип возврата отличается.
Предполагая, что контент точно такой же, только он включается из нескольких заголовков, компилятор потребует дополнительной вычислительной мощности и памяти для отслеживания уже включенного кода.
Таким образом, это была бы склонная к ошибкам и неэффективная