Базовая информация
Перед тем как программа будет скомпилирована, она должна быть отредактирована препроцессором. Команды, предназначенные для препроцессора, называются директивы препроцессора. Они будут подробно описаны в главе о О препроцессоре.
Сейчас нас будет интересовать только директива #include
.
Программа test.c
начинается со строки:
#define <stdio.h>
Эта директива означает, что информация из файла stdio.h
должна быть включена в текст программы перед тем, как программа будет передана процессу компиляции.
Файл stdio.h
содержит информацию из стандартной библиотеки Си, которая отвечает за операции ввода/вывода (I/O). Наименование заголовочных файлов из стандартной библиотеки принято заключать между символами <
и >
. Для пользовательских библиотек используется символ "
.
Таких заголовочных файлов (хэдеров) достаточно много, хотя многие из них появились уже в процессе развития языка и не входили в первый стандарт языка (C89).
Стандарт C89 состоял из:
<assert.h> <limits.h> <signal.h> <stdlib.h>
<ctype.h> <locale.h> <stdarg.h> <string.h>
<errno.h> <math.h> <stddef.h> <time.h>
<float.h> <setjmp.h> <stdio.h>
В С94 было добавлено еще 3:
<iso646.h> <wchar.h> <wctype.h>
В С99 появилось 6 дополнительных хэдеров:
<complex.h> <inttypes.h> <stdint.h> <tgmath.h>
<fenv.h> <stdbool.h>
Еще 5 добавилось с приходом стандарта С11:
<stdalign.h> <stdatomic.h> <stdnoreturn.h> <threads.h> <uchar.h>
И наконец, в текущей версии языка С23 присутствуют еще два заголовочных файла:
<stdbit.h> <stdckdint.h>
Некоторые считаются в текущий момент устаревшими:
<stdalign.h> <stdbool.h> <stdnoreturn.h>
Разработка на UNIX системах требует следование стандарту POSIX, который подразумевает использование следующих хэдеров:
<aio.h> <libgen.h> <spawn.h> <sys/time.h>
<arpa/inet.h> <limits.h> <stdarg.h> <sys/times.h>
<assert.h> <locale.h> <stdbool.h> <sys/types.h>
<complex.h> <math.h> <stddef.h> <sys/uio.h>
<cpio.h> <monetary.h> <stdint.h> <sys/un.h>
<ctype.h> <mqueue.h> <stdio.h> <sys/utsname.h>
<dirent.h> <ndbm.h> <stdlib.h> <sys/wait.h>
<dlfcn.h> <net/if.h> <string.h> <syslog.h>
<errno.h> <netdb.h> <strings.h> <tar.h>
<fcntl.h> <netinet/in.h> <stropts.h> <termios.h>
<fenv.h> <netinet/tcp.h> <sys/ipc.h> <tgmath.h>
<float.h> <nl_types.h> <sys/mman.h> <time.h>
<fmtmsg.h> <poll.h> <sys/msg.h> <trace.h>
<fnmatch.h> <pthread.h> <sys/resource.h> <ulimit.h>
<ftw.h> <pwd.h> <sys/select.h> <unistd.h>
<glob.h> <regex.h> <sys/sem.h> <utime.h>
<grp.h> <sched.h> <sys/shm.h> <utmpx.h>
<iconv.h> <search.h> <sys/socket.h> <wchar.h>
<inttypes.h> <semaphore.h> <sys/stat.h> <wctype.h>
<iso646.h> <setjmp.h> <sys/statvfs.h> <wordexp.h>
<langinfo.h> <signal.h>
Разработка под Windows потребует свое множество хэдеров для разработки.
Здесь мы столкнулись с первым минусом языка Си - трудоемкую работу для создания переносимых приложений. Это та цена, которую требуется заплатить за близость к “железу”.
<stdio.h>
включен в текст программы по той причине, что в отличии от многих других языков программирования, Си не имеет встроенных команд для чтения и записи данных. Это возможность предоставляется функцией из стандартной библиотеки.
Директивы всегда должны начинаться с символа #
, который отделяет директивы от других синтаксических конструкций языка, тем самым позволяя препроцессору легко вычленить их и выполнить свою работу. Каждая директива, по умолчанию, записывается в одну строку. Никаких других маркеров обозначающих конец строки не существует.
Более точно и строго использование директив описывает стандарт языка.
Дополнительная информация
Директива
#include
должна идентифицировать заголовочный или исходный файл
Термин исходный файл ранее не встречался. Поясним его: до этого мы говорили о стандартных хэдерах, но в последствии, работая над большими проектами, мы будем структурировать наше приложение и писать свои собственные модули. Здесь под исходным файлом следует понимать собственный модуль, который не является частью стандарта.
Директива препроцессинга, имеющая форму:
# include < h-char-sequence > new line
ищет указанную последовательность символов (как имя файла в месте определенном реализаций), которая однозначно идентифицируется положением между разделителями
<
и>
и осуществляет замену всей директивы полным содержимым заголовочного файла.
new line
здесь означает символ перехода на новую строку. Т.е. как и было описано ранее - любая директива записывается в одну строку.
h-char-sequence
- означает последовательность из h-char
, где h-char
символ из множества:
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9
_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’
за исключением new line
и >
Использование символов в последовательности h-char-sequence
'
"
\
//
/*
может привести к непредсказуемому поведению программы.
Директива препроцессинга, имеющая форму:
# include " q-char-sequence " new line
ищет указанную последовательность символов (как имя файла в месте определенном реализаций), которая однозначно идентифицируется положением между разделителями"
и"
и осуществляет замену всей директивы полным содержимым заголовочного файла. Если такой поиск не поддерживается или не выполнился, директива трактуется как# include < h-char-sequence > new line
q-char-sequence
означает последовательность из q-char
, где q-char
символ из множества:
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9
_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’
за исключением символов new line
и "
.
Использование символов в последовательности h-char-sequence
'
\
//
/*
может привести к непредсказуемому поведению программы.
Наименование до
.
должно быть уникально. Наименование предпочтительно не должно начинаться с цифры. Регистр может игнорироваться
Импортируемые с помощью директивы
include
файлы, так же могут содержать директиву препроцессораinclude
, уровень вложенности зависит от реализации препроцессора/компилятора.