Базовая информация

Перед тем как программа будет скомпилирована, она должна быть отредактирована препроцессором. Команды, предназначенные для препроцессора, называются директивы препроцессора. Они будут подробно описаны в главе о О препроцессоре.

Сейчас нас будет интересовать только директива #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, уровень вложенности зависит от реализации препроцессора/компилятора.