1.3. Стандартная библиотека ввода/вывода (stdio.H).
Традиционной для ОС UNIX библиотекой функций более высокого уровня, чем библиотека системных вызовов, является, так называемая, стандартная библиотека ввода/вывода (stdio). Основной набор функций этой библиотеки служит для выполнения файловых операций с буферизацией данных в памяти пользовательского процесса. Библиотека ввода/вывода фактически стандартизована очень давно, и ей можно безопасно пользоваться в любой операционной среде. В частности, единообразные библиотеки ввода/вывода поддерживаются во всех современных реализациях системы программирования языка Си, выполненных не в среде ОС UNIX (включая реализации в среде MS-DOS).
Для решения этих проблем была построена специальная библиотека функций, названная stdio — «стандартная библиотека ввода/вывода» (standard input/output library). Она является частью библиотеки /lib/libc.a и представляет собой надстройку над системными вызовами (т.к. в конце концов все ее функции время от времени обращаются к системе, но гораздо реже, чем если использовать системные вызовы непосредственно). Директива #include <stdio.h> включает в нашу программу файл с объявлением форматов данных и констант, используемых этой библиотекой.
1.3.1. Особенности работы с stdio.H.
Библиотеку stdio можно назвать библиотекой буферизованного обмена, а также библиотекой работы с текстовыми файлами (т.е. имеющими разделение на строки), поскольку для оптимизации обменов с диском (для уменьшения числа обращений к нему и тем самым сокращения числа системных вызовов) эта библиотека вводит буферизацию, а также предоставляет несколько функций для работы со строчно-организованными файлами.
Связь с файлом в этой модели обмена осуществляется уже не при помощи целого числа — дескриптора файла (file descriptor), а при помощи адреса «связной» структуры FILE. Указатель на такую структуру условно называют указателем на файл (file pointer). Структура FILE содержит в себе:
• дескриптор fd файла для обращения к системным вызовам;
• указатель на буфер, размещенный в памяти программы;
• указатель на текущее место в буфере, откуда надо выдать или куда записать очередной символ; этот указатель продвигается при каждом вызове getc или putc;
• счетчик оставшихся в буфере символов (при чтении) или свободного места (при записи);
• режимы открытия файла (чтение/запись/чтение+запись) и текущее состояние файла. Одно из состояний — при чтении файла был достигнут его конец;
Предусмотрено несколько стандартных структур FILE, указатели на которые называются stdin, stdout и stderr и связаны с дескрипторами 0, 1, 2 соответственно (стандартный ввод, стандартный вывод, стандартный вывод ошибок). Напомним, что эти каналы открыты неявно (автоматически) и, если не перенаправлены, связаны с вводом с клавиатуры и выводом на терминал.
Буфер в оперативной памяти нашей программы создается (функцией malloc) при открытии файла при помощи функции fopen(). После открытия файла все операции обмена с файлом происходят не по 1 байту, а большими порциями размером с буфер — обычно по 512 байт (константа BUFSIZ).
При чтении символа:
в буфер считывается read-ом из файла порция информации, и getc выдает ее первый байт. При последующих вызовах getc выдаются следующие байты из буфера, а обращений к диску уже не происходит! Лишь когда буфер будет исчерпан — произойдет очередное чтение с диска. Таким образом, информация читается из файла с опережением, заранее наполняя буфер; а по требованию выдается уже из буфера. Если мы читаем 1024 байта из файла при помощи getc(), то мы 1024 раза вызываем эту функцию, но всего 2 раза системный вызов read — для чтения двух порций информации из файла, каждая — по 512 байт.
выводимые символы накапливаются в буфере. Только когда в нем окажется большая порция информации, она за одно обращение write записывается на диск. Буфер записи «выталкивается» в файл в таких случаях:
— буфер заполнен (содержит BUFSIZ символов).
— при закрытии файла (fclose или exit).
— при вызове функции fflush.
— в специальном режиме — после помещения в буфер символа ‘\n’ .
— в некоторых версиях — перед любой операцией чтения из канала stdin (например, при вызове gets), при условии, что stdout буферизован построчно (режим _IOLBF), что по умолчанию так и есть.
Препроцессор
Препроцессор является обязательным компонентом компилятора языка Си. Вообще весь процесс компиляции программы на языке Си разбивается на три этапа:
Препроцессор обрабатывает исходный текст программы до ее непосредственной компиляции. Результатом работы препроцессора является полный текст программы, который передается на компиляцию в исполняемый файл.
Затем компилятор компилирует обработанный препроцессором исходный код в объектные файлы.
И на последнем этапе линкер (линковщик) объединяет (линкует) объектные файлы в один исполняемый файл или файл динамической библиотеки.
Для управления препроцессором применяются директивы, каждая из которых начинается с символа решетки # и располагается на отдельной строке. Препроцессор просматривает текст программы, находит эти директивы и должным образом обрабатывает их.
Мы можем использовать следующие директивы:
#define : определяет макрос или препроцессорный идентификатор
#undef : отменяет определение макроса или идентификатора
#ifdef : проверяет, определен ли идентификатор
#ifndef : проверяет неопределенности идентификатор
#include : включает текст из файла
#if : проверяет условие выражение (как условная конструкция if)
#else : задает альтернативное условие для #if
#endif : окончание условной директивы #if
#elif : задает альтернативное условие для #if
#line : меняет номер следующей ниже строки
#error : формирует текст сообщения об ошибке трансляции
#pragma : определяет действия, которые зависят от конкретной реализации компилятора
# : пустая директива, по сути ничего не делает
Рассмотрим основные из этих директив.
Директива #include. Включение файлов
Ранее уже использовалась директива #include . Эта директива подключает в исходный текст файлы. Она имеет следующие формы применения:
Например, если нам надо задействовать в приложении консольный ввод-вывод с помощью функций printf() или scanf() , то нам надо подключить файл «stdio.h», который содержит определение этих функций:
При выполнении этой директивы препроцессор вставляет текст файла stdio.h . Данный файл еще называется заголовочным. Заголовочные файлы содержат прототипы функций, определения и описания типов и констант и имеют расширение .h .
Поиск файла производится стандартных системных каталогах. Вообще есть стандартный набор встроенных заголовочных файлов, который определяется стандартом языка и которые мы можем использовать:
assert.h : отвечает за диагностику программ
complex.h : для работы с комплексными числами
ctype.h : отвечает за преобразование и проверку символов
errno.h : отвечает за проверку ошибок
fenv.h : для доступа к окружению, которое управляет операциями с числами с плавающей точкой
float.h : отвечает за работу с числами с плавающей точкой
inttypes.h : для работы с большими целыми числами
iso646.h : содержит ряд определений, которые расширяют ряд логических операций
limits.h : содержит предельные значения целочисленных типов
locale.h : отвечает за работу с локальной культурой
math.h : для работы с математическими выражениями
setjmp.h : определяет возможности нелокальных переходов
signal.h : для обработки исключительных ситуаций
stdalign.h : для выравнивания типов
stdarg.h : обеспечивает поддержку переменного числа параметров
stdatomic.h : для выполнения атомарных операций по разделяемым данным между потоками
stdbool.h : для работы с типом _Bool
stddef.h : содержит ряд вспомогательных определений
stdint.h : для работы с целыми числами
stdio.h : для работы со средствами ввода-вывода
stdlib.h : содержит определения и прототипы функций общего пользования
stdnoreturn.h : содержит макрос noreturn
string.h : для работы со строками
tgmath.h : подключает math.h и complex.h плюс добавляет дополнительные возможности по работе с математическими вычислениями
threads.h : для работы с потоками
time.h : для работы с датами и временем
uchar.h : для работы с символами в кодировке Unicode
wchar.h : для работы с символами
wctype.h : содержит дополнительные возможности для работы с символами
Однако стоит отметить, что в различных средах к этому набору могут добавляться дополнительные встроенные заголовочные файлы для тех или иных целей, например, для работы с графикой.
Определение заголовочных файлов
Кроме стандартных заголовочных файлов мы можем подключать и свои файлы. Например, в той же папке, где находиться главный файл программы, определим еще один файл, который назовем numbers.c .
Определим в нем следующий код:
Здесь просто определена одна переменная. Теперь подключим этот файл в главный файл программы, который, допустим, называется app.c :
При подключении своих файлов их имя указывается в кавычках. И несмотря на то, что в программе не определена переменная number, она будет браться из подключенного файла numbers.c . Но опять же отмечу, важно, что в данном случае файл numbers.c располагается в одной папке с главным файлов программы.
Определение заголовочных файлов
В то же время данный способ прекрасно работает в GCC. Но для разных сред программирования способ подключения файлов может отличаться. Например, в Visual Studio мы получим ошибку. И более правильный подход будет состоять в том, что определить объявление объекта (переменной/константы) или функции в дополнительном заголовочном файле, а определение объекта или функции поместить в стандартный файл с расширением .c .
Например, в нашем в файле numbers.c уже есть определение переменной number. Теперь в ту же папку добавим новый файл numbers.h — файл с тем же названием, но другим расширением.

И определим в numbers.h следующий код:
Ключевое слово extern указывает, что данный объект является внешним. И в этом случае мы могли бы его подключить в файл исходного кода:
Далее чтобы скомпилировать эту программу, передадим компилятору GCC оба файла с исходным кодом — app.c и numbers.c :
Компилятору передаются файлы через пробел и компилируются в один исполняемый файл.
Примечание для Visual Studio
Если разработка ведется в Visual Studio , то не надо подключать файл с исходным кодом ( numbers.c ), однако чтобы Visual Studio видела функционал файла в процессе разработки, может потребоваться подключить заголовочный файл ( numbers.h ). При этом заголовочный файл numbers.h помещается в папку Headers Files .
What does '#include <stdio.h>' really do in a C program
I am new to c programming and I was coding some simple programs «Hello world» style.
In all of these programs I put #include<stdio.h> in the top but I am not sure what this means exactly. I googled it and I found that stdio.h is a file that has commands for the preprocessor, but what is a preprocessor? I thought when I write code, I compile it and my code transforms to a form that a «computer» can read and then I can run it. Can somebody explain to me what the usage of this command is?
![]()
4 Answers 4
It looks for the stdio.h file and effectively copy-pastes it in the place of this #include statements. This file contains so-called function prototypes of functions such as printf() , scanf() , . so that compiler knows what are their parameters and return values.
The simplest explanation perhaps should be that your program calls or uses many functions whose code is not part of your program itself. For e.g. if you write «printf» in your code to print something, the compiler does not know what to do with that call.
stdio.h is the place where information for that printf resides.
Update:
Rather the prototype of printf function (name, return type and parameters) reside in stdio.h. That is all required in the compilation phase. The actual code of printf is included in the linking phase, which comes after compilation.
The include statement basically inserts all function prototypes BEFORE the actual compilation. Hence the name preprocessor.
Update 2:
Since the question focused on include statement (and the OP also asked about writing definition of functions himself, another important aspect is if it is written like (note the angular brackets)
The preprocessor assumes, it is a standard library header and looks in the system folders first where the compiler has been installed.
If instead a programmer defines a function by himself and place the .h file in the current working directory, he would use (note the double quotes)
Following illustrates it and the behavior is portable across all platforms.
Язык С: структура программы
Программа на языке C состоит из нескольких частей. Вот основные из них:
- команды препроцессора;
- функции;
- переменные;
- операторы и выражения;
- комментарии.
Взгляните на простой код, который будет печатать слова “Hello World”:
Рассмотрим каждую часть приведенной выше программы:
- Первая строка программы #include <stdio.h> — это команда препроцессора, которая указывает компилятору языка C включить файл stdio.h, прежде чем приступить к компиляции.
- Следующая строка int main() — это главная функция, с которой начинается выполнение программы.
- Следующая строка /*…*/ будет проигнорирована компилятором. Она потребовалась для добавления дополнительных комментариев в программу. Поэтому такие строки называются комментариями в программе.
- Следующая строка printf(…) — это еще одна функция, доступная в C, которая вызывает вывод на экран сообщения “Hello, World!”.
- Следующая строка return 0 завершает функцию main() и возвращает значение 0 .
Компиляция и выполнение программы
Теперь посмотрим, как сохранить исходный код в файле, а также как его скомпилировать и запустить: