Стандартная библиотека языка Си
Стандартной библиотекой языка Си (также известная как libc, crt) называется часть стандарта ANSI C, посвященная заголовочным файлам и библиотечным подпрограммам. Является описанием реализации общих операций, таких как обработка ввода/вывода и строк, в языке программирования Си. Стандартная библиотека языка Си — это описание программного интерфейса, а не настоящая библиотека, пригодная для использования в процессе компиляции.
Содержание
Структура
Имя и характеристики каждой функции указываются в файле, именуемым заголовочным файлом, но текущая реализация функций описана отдельно в библиотечном файле. Наименование и возможности заголовочных файлов становятся общими, но организация библиотек по-прежнему остается разнотипной. Стандартная библиотека обычно поставляется вместе с компилятором. Так как компиляторы языка Си часто обеспечивают расширенную функциональность, не определенную стандартом ANSI C, стандартная библиотека одного компилятора несовместима со стандартными библиотеками других компиляторов.
Мнения о структуре
Большая часть стандартной библиотеки языка Си производит впечатление удачно спроектированной. Некоторые отдельные части, дававшие преимущество в прошлом, могут провоцировать ошибки. Функции строкового ввода gets() (и применение scanf() для считывания вводимых строк) являются источником множества переполнений буфера, поэтому большинство руководств по программированию советуют избегать подобных приемов. Функция strcpy() также весьма печально знаменита. Ещё одной неоднозначной функцией является strtok() — функция, разработанная как простейший лексический анализатор, но имеющая множество «подводных камней» и потому весьма трудная в использовании.
Выбор применения типа size_t вместо int для числа элементов, указанных для fread() и fwrite() является несовместимым с разработанной общей семантикой для size_t (для представления количества байтов). [источник не указан 506 дней]
stdio достаточно ограничен (слишком высокий уровень для использования во многих ситуациях) и стандарт не позволяет пользователю самостоятельно переназначать или расширять его свойства. В итоге, для множества приложений разрабатываются собственные библиотеки-обертки вокруг механизмов более низкого уровня и функций, реализуемых ОС, таких как POSIX. Например, stdio не работают с сигналами или асинхронными непакетными режимами ввода-вывода, которые широко используются в сетевых серверах. В итоге, только серверы, использующие модель целого процесса для клиента, могут полностью полагаться на функции stdio для обслуживания их на POSIX-совместимых системах в пакетном режиме ввода-вывода.
Определенных функций стандартной библиотеки нужно избегать при разработке многопоточных приложений. Примитивы контроля потоков предназначались для оставшейся части ОС и игнорируют общие стандарты, как например, библиотеку потоков POSIX, ожидая, что программисты языка Си, самостоятельно об этом позаботятся работая с повторным использованием и синхронизацией. Ни язык Си, ни его стандартная библиотека никак не проверяют подобные системноспецифичные результаты.
История
Язык программирования Си до стандартизации не обеспечивал встроенной функциональности, как, например, операции ввода-вывода (в отличие от таких традиционных языков, как Кобол и Фортран). Позже, в сообществе программистов, работавших с языком Си, зародились идеи, которые реализовались в то, что мы сейчас называем Стандартной библиотекой языка Си, для поддержки этой функциональности. Большинство этих идей в итоге объединились в определении стандарта языка программирования Си.
И Unix, и Си были созданы в AT&T’s Bell Laboratories в конце 1960-х — начале 1970-х. В 1970-е язык программирования Си начал пользоваться невероятной популярностью. Множество университетов и организаций начали создание собственных вариантов языка, более подходящих для собственных нужд. С началом 1980-х проблемы совместимости между различными реализациями языка Си стали слишком очевидными. В 1983 году Американский национальный институт стандартов (ANSI) сформировал комитет для принятия стандарта языка Си, известный как «ANSI Си». Эта работа вылилась в создание так называемого стандарта C89 в 1989. Часть итогового стандарта была набором библиотек, названная Стандартная библиотека ANSI Си.
Последующие версии стандарта языка Си добавляли некоторые новые и наиболее полезные заголовочные файлы в библиотеку. Поддержка этих новых расширений зависела от реализации.
Заголовочные файлы <iso646.h>, <wchar.h> и <wctype.h> были добавлены в Приложении 1 (сокращенно NA1) — дополнении к Стандарту языка Си, ратифицированному в 1995.
Заголовочные файлы <complex.h>, <fenv.h>, <inttypes.h>, <stdbool.h>, <stdint.h> и <tgmath.h> были добавлены в C99, версии Стандарта языка Си, опубликованном в 1999.
Стандарт ANSI
Стандартная библиотека ANSI Си состоит из 24 заголовочных файлов, каждый из которых можно подключать к программному проекту при помощи одной директивы. Каждый заголовочный файл содержит объявления одной или более функций, определения типов данных и макросы. Содержание этих заголовочных файлов перечисляется ниже.
В сравнении с некоторыми другими языками (например Java) стандартная библиотека крайне мала. Библиотека обеспечивает поддержку основного набора математических функций, обработку строк, конвертацию типов, файловый и консольный ввод-вывод. Она не содержит стандартный набор «контейнерных типов» как стандартная библиотека шаблонов языка C++, компоненты для работы с графическим пользовательским интерфейсом (GUI), сетью и прочей разнообразной функциональности, которую Java поддерживает по стандарту. Главным преимуществом маленькой стандартной библиотеки является упрощение работы с окружением ANSI Си по сравнению с другими языками, а следовательно и упрощение портирования программ на языке Си на новые платформы.
Множество прочих библиотек было разработано для поддержки схожей функциональности, обеспечиваемой другими языками в их стандартных библиотеках. Например, в проекте разработки окружения рабочего стола GNOME был разработан набор графических инструментов GTK+ и GLib — библиотека контейнерных структур данных, как впрочем и множество других известных примеров. Разнообразие доступных библиотек означает, что некоторые инструменты верхнего уровня, со временем подтвердили свою полезность. Значительным минусом является то, что они часто не слишком успешно взаимодействуют друг с другом, поэтому программистам зачастую привычнее работать с различными наборами библиотек, а их наборы могут быть доступны на различных специфических платформах.
Библиотечные заголовочные файлы ANSI Си
| <assert.h> | Содержит макрос утверждений, используемый для обнаружения логических и некоторых других типов ошибок в отлаживаемой версии программы. |
| <complex.h> | Набор функций для работы с комплексными числами. (Появилось в C99) |
| <ctype.h> | Содержит функции, используемые для классификации символов по их типам или для конвертации между верхним и нижним регистрами независимо от используемой кодировки (обычно ASCII или одно из её расширений, хотя есть и реализации, использующие EBCDIC). |
| <errno.h> | Для проверки кодов ошибок, возвращаемых библиотечными функциями. |
| <fenv.h> | Для управления средой, использующей числа с плавающей запятой. (Появилось в C99) |
| <float.h> | Содержит заранее определенные константы, описывающие специфику реализации свойств библиотеки для работы с числами с плавающей запятой, как, например, минимальная разница между двумя различными числами с плавающей точкой (_EPSILON), максимальное число цифр точности (_DIG) и область допустимых чисел (_MIN, _MAX). |
| <inttypes.h> | Для точной конвертации целых типов. (Появилось в C99) |
| <iso646.h> | Для программирования в кодировке ISO 646. (Появилось в NA1) |
| <limits.h> | Содержит заранее заданные константы, определяющие специфику реализации свойств целых типов, как, например, область допустимых значений (_MIN, _MAX). |
| <locale.h> | Для setlocale() и связанных констант. Используется для выбора соответствующего языка. |
| <math.h> | Для вычисления основных математических функций |
| <setjmp.h> | Объявляет макросы setjmp и longjmp, используемые для нелокальных переходов |
| <signal.h> | Для управления обработкой сигналов |
| <stdarg.h> | Для доступа к различному числу аргументов, переданных функциям. |
| <stdbool.h> | Для булевых типов данных. (Появилось в C99) |
| <stdint.h> | Для определения различных типов целых чисел. (Появилось в C99) |
| <stddef.h> | Для определения нескольких стандартных типов и макросов. |
| <stdio.h> | Реализует основные возможности ввода и вывода в языке Си. Этот файл содержит весьма важную функцию printf . |
| <stdlib.h> | Для выполнения множества операций, включая конвертацию, генерацию псевдослучайных чисел, выделение памяти, контроль процессов, окружения, сигналов, поиска и сортировки. |
| <string.h> | Для работы с различными видами строк. |
| <tgmath.h> | Для типовых математических функций. (Появилось в C99) |
| <time.h> | Для конвертации между различными форматами времени и даты. |
| <wchar.h> | Для обработки «широких» потоков и нескольких видов строк при помощи «широких» символов (поддержка набора языков). (Появилось в NA1) |
| <wctype.h> | Для классификации «широких» символов. (Появилось в NA1) |
Стандартная библиотека Си в других языках
Некоторые языки обеспечивают функциональность стандартной библиотеки Си при помощи своих собственных библиотек. Библиотека может быть адаптирована для структур языка, но семантика операций остается схожей. Язык программирования C++, например, содержит функциональность стандартной библиотеки ANSI Си в пространстве имен std (например, std::printf , std::atoi , std::feof и т. д.), в заголовочных файлах со схожими именами как в Си (« cstdio », « cmath », « cstdlib » и т. д.). К другим языкам, в которых применяются схожие подходы относятся, например, D и Python. В последнем, к примеру, встроенный объект file определен как «реализованный при помощи пакета stdio языка Си»[1], так что доступные операции (открытие, чтение, запись и т. д.), как ожидается, должны иметь поведение как у соответствующих функий языка Си.
Общая поддержка библиотек
Пока не стандартизировано, но программы на языке Си могут зависеть от библиотек подпрограмм, которые содержат код, используемый компилятором во время работы. Код, инициализирующий процесс для операционной системы, например, перед вызовом main() , реализован в библиотеке времени исполнения языка Си (C Run-Time Library, CRT) для данной версии компилятора. Код библиотеки CRT может помочь с реализацией других особенностей языка, как например, обработка неперехваченных исключений или реализация работы с числами с плавающей точкой.
Стандартная библиотека языка Си только регламентирует наличие вышеупомянутых подпрограмм и их поведение. Так как реализация компилятора может зависеть от наличия этих дополнительных функций, то все зависит от того, какие подпрограммы собраны в Стандартную библиотеку языка Си, таким образом любая программа, разработанная с их помощью, будет нуждаться в них.
Хотя часто путают их со Стандартной библиотекой языка Си из-за их комплектации, библиотека CRT не является стандартизированной частью языка и зависит от особенностей поставки программного продукта.
Встроенные функции компилятора
Некоторые компиляторы (например, GCC [1] ) поддерживают внутренние версии множества функций Стандартной библиотеки языка Си; то есть, реализации функций записываются в компилируемый объектный модуль, а программа вызывает внутренние версии вместо функций общей библиотеки Си. Это уменьшает накладные расходы при вызове функции, особенно если вызов функции заменяется встроенными вариантами, и разрешается использование других форм оптимизации (если компилятор поддерживает управление характеристиками внутренних вариантов), но может приводить к проблемами при отладке (например, внутренние версии не могут быть заменены инструментальными версиями для проверки).
Стандартная библиотека POSIX
POSIX (и SUS) определяют число подпрограмм, которые могут быть доступны помимо определенных в Стандартной библиотеке языка Си; они часто реализуют аналогичную функциональность со Стандартной библиотекой с различной степенью схожести. Например, glibc реализует такие функции как fork в libc.so, но перед вызовом библиотеки потоков объединяются в glibc, хотя это заявлено как отдельная библиотека с собственным флагом линковщика. Зачастую, такая POSIX-совместимая функциональность рассматривается как часть библиотеки; соответствующая библиотека Си, таким образом, может быть идентифицирована как ANSI или ISO библиотека языка Си.
Реализации
Существует множество реализаций, поставляемых как с различными операционными системами, так и с компиляторами языка Си. На BSD системах, к примеру, системная библиотека встроена в операционную систему и поддерживается общим репозиторием исходников. На большинстве систем библиотека может быть найдена под именем « libc ».
Хотя существует очень много реализаций, вот небольшой список самых популярных библиотек:
Приложение B. Стандартная библиотека
Настоящее приложение представляет собой краткое изложение библиотеки, утвержденной в качестве ANSI-стандарта. Сама по себе библиотека не является частью языка, однако заложенный в ней набор деклараций функций, а также определений типов и макросов составляет системную среду, поддерживающую стандарт Си. Мы не приводим несколько функций с ограниченной областью применения — те, которые легко синтезируются из других функций, а также все то, что касается многобайтовых литер и специфики, обусловленной языком, национальностью и культурой.
Функции, типы и макросы декларируются в следующих головных файлах:
| <assert.h> | <float.h> | <math.h> | <stdarg.h> | <stdlib.h> |
| <ctype.h> | <limits.h> | <setjmp.h> | <stddef.h> | <string.h> |
| <errno.h> | <locale.h> | <signal.h> | <stdio.h> | <tlme.h> |
Доступ к головному файлу осуществляется при помощи строки препроцессора
Головные файлы можно включать в любом порядке и сколько угодно раз. Строка #include не должна быть внутри внешней декларации или определения и должна встретиться раньше, чем что-нибудь будет востребовано из включаемого головного файла. В конкретной реализации головной файл может и не быть исходным файлом.
Для использования в библиотеке зарезервированы внешние идентификаторы, начинающиеся со знака подчеркивания, а также все другие идентификаторы, начинающиеся с двух знаков подчеркивания или с подчеркивания и заглавной буквы.
B1. Ввод–вывод: <stdio.h>
Определенные в <stdio.h> функции ввода-вывода, а также типы и макросы составляют приблизительно треть библиотеки.
Поток — это источник или получатель данных; его можно связать с диском или с каким-то другим внешним устройством. Библиотека поддерживает два вида потоков: текстовый и бинарный, хотя на некоторых системах, в частности в UNIX`е, они не различаются. Текстовый поток — это последовательность строк; каждая строка имеет нуль или более литер и заканчивается литерой ‘\n’ . Операционная среда может потребовать коррекции текстового потока (например, перевода ‘\n’ в литеры возврат_каретки и перевод_строки).
Бинарный поток — это последовательность непреобразуемых байтов, представляющих собой некоторые промежуточные данные, которые обладают тем свойством, что, если их записать, а затем прочесть той же системой ввода-вывода, то мы получим информацию, совпадающую с исходной.
Поток соединяется с файлом или устройством посредством его открытия, указанная связь разрывается путем закрытия потока. Открытие файла возвращает указатель на объект типа FILE , который содержит всю информацию, необходимую для управления этим потоком. Если не возникает двусмысленности, мы будем пользоваться терминами «файловый указатель» и «поток» как равнозначными.
Когда программа начинает работу, уже открыты три потока: stdin , stdout и stderr .
B1.1. Операции над файлами
Ниже перечислены функции, оперирующие с файлами. Тип size_t — беззнаковый целочисленный тип, используемый для описания результата оператора sizeof .
fopen открывает файл с заданным именем и возвращает поток или NULL , если попытка открытия оказалась неудачной. Режим mode допускает следующие значения:
| "r" | текстовый файл открывается для чтения (от read (англ.) — читать) |
| "w" | текстовый файл создается для записи; старое содержимое (если оно было) выбрасывается (от write (англ.) — писать) |
| "a" | текстовый файл открывается или создается для записи в конец файла (от append (англ.) — добавлять) |
| "r+" | текстовый файл открывается для исправления (т.е. для чтения и для записи) |
| "w+" | текстовый файл создается для исправления; старое содержимое (если оно было) выбрасывается |
| "a+" | текстовый файл открывается или создается для исправления уже существующей информации и добавления новой в конец файла |
Режим «исправления» позволяет читать и писать в один и тот же файл; при переходах от операций чтения к операциям записи и обратно должны осуществляться обращения к fflush или к функции позиционирования файла. Если указатель режима дополнить буквой b (например, "rb" или "w+b" ), то это будет означать, что файл бинарный. Ограничение на длину имени файла задано константой FILENAME_MAX . FOPEN_MAX ограничивает число одновременно открытых файлов.
freopen открывает файл с указанным режимом и связывает его с потоком stream . Она возвращает stream или, в случае ошибки, NULL . Обычно freopen используется для замены файлов, связанных с stdin , stdout или stderr , другими файлами.
Применяемая к потоку вывода fflush производит дозапись всех оставшихся на буфере (еще не записанных) данных; для потока ввода эта функция не определена. Возвращает EOF в случае возникшей при записи ошибки или нуль в противном случае. Обращение вида fflush(NULL) выполняет указанные операции для всех потоков вывода.
fclose производит дозапись еще незаписанных буферизованных данных, сбрасывает нечитанный буферизованный ввод, освобождает все автоматически запрошенные буфера, после чего закрывает поток. Возвращает EOF в случае ошибки и нуль в противном случае.
remove удаляет файл с указанным именем; последующая попытка открыть файл с этим именем вызовет ошибку. Возвращает ненулевое значение в случае неудачной попытки.
rename заменяет имя файла; возвращает ненулевое значение в случае, если попытка изменить имя оказалась неудачной. Первый параметр задает старое имя, второй — новое.
tmpfile создает временный файле режимом доступа "wb+" , который автоматически удаляется при его закрытии или обычном завершении программой своей работы. Эта функция возвращает поток или, если она не смогла создать файл, NULL .
tmpnam(NULL) создает стринг, который не совпадает ни с одним из имен существующих файлов, и возвращает указатель на внутренний статический массив. tmpnam(s) запоминает стринг в s и возвращает его в качестве значения функции; длина s должна быть не менее L_tmpnam . При каждом вызове tmpnam генерируется новое имя; при этом гарантируется не более TMP_MAX различных имен за один сеанс работы программы. Заметим, что tmpnam создает имя, но не файл.
setvbuf управляет буферизацией потока; к ней следует обращаться прежде, чем будет выполняться чтение, запись или какая-либо другая операция. Режим mode со значением _IOFBF вызывает полную буферизацию, с _IOLBF — «построчную» буферизацию текстового файла, а режим _IONBF отменяет всякую буферизацию. Если параметр buf не есть NULL , то его значение — указатель на буфер, в противном случае под буфер будет запрашиваться память. Параметр size задает размер буфера. Функция setvbuf в случае ошибки выдает ненулевое значение.
Если buf есть NULL , то для потока stream буферизация выключается. В противном случае вызов setbuf приведет к тем же действиям, что и вызов (void) setvbuf(stream, buf, _IOFBF, BUFSIZ) .
B1.2. Форматный вывод
Функции printf осуществляют вывод информации по формату.
fprintf преобразует и пишет вывод в поток stream под управлением format . Возвращаемое значение — число записанных литер или, в случае ошибки, отрицательное значение.
Форматный стринг содержит два вида объектов: обычные литеры, копируемые в выводной поток, и спецификации преобразования, которые вызывают преобразование и печать остальных аргументов в том порядке, как они перечислены. Каждая спецификация преобразования начинается с % и заканчивается литерой — спецификатором преобразования. Между % и литерой — спецификатором в порядке, в котором они здесь перечислены, могут быть расположены следующие элементы информации:
- Флаги (в любом порядке), модифицирующие спецификацию:
- — указывает на то, что преобразованный аргумент должен быть прижат к левому краю поля.
- + предписывает печатать число всегда со знаком.
- пробел — если первая литера — не знак, то числу должен предшествовать пробел.
- 0 указывает, что числа должны дополняться ведущими нулями до всей ширины поля.
- # указывает на одну из следующих форм вывода: для o первой цифрой должен быть 0; для x или X ненулевому результату должны предшествовать 0x или 0X ; для e , E , f , g и G вывод должен всегда содержать десятичную точку; для g и G хвостовые нули не отбрасываются.
Ширина, или точность, или обе характеристики могут быть специфицированы при помощи * ; в этом случае необходимое число «извлекается» из следующего аргумента, который должен иметь тип int (в случае двух звездочек используются два аргумента).
Литеры–спецификаторы и разъяснение их смысла приведены в табл. B.1. Если за % нет правильной литеры–спецификатора, результат не определен.
Таблица B.1. Преобразования printf
Литера Тип аргумента; вид печати d, i int ; знаковая десятичная запись. o int ; беззнаковая восьмеричная запись (без ведущего 0). x, X int ; беззнаковая шестнадцатиричная запись (без ведущих 0x или 0X ), в качестве цифр от 10 до 15 используются abcdef для x и ABCDEF для X . u int ; беззнаковое десятичное целое. c int ; единичная литера после преобразования в unsigned char . s char * ; литеры стринга печатаются, пока не встретится ‘\0’ или не исчерпается количество литер, указанное точностью. f double ; десятичная запись вида [-]mmm.ddd , где количество d специфицируется точностью. По умолчанию точность равна 6; нулевая точность подавляет печать десятичной точки. e, E double ; десятичная запись вида [-]m.dddddde+-xx или запись вида [-]m.ddddddE+-xx , где количество d специфицируется точностью. По умолчанию точность равна 6; нулевая точность подавляет печать десятичной точки. g, G double ; используется %e и %E , если экспонента меньше -4 или больше или равна точности; в противном случае используется %f . Хвостовые нули и точка в конце не печатаются. p void * ; печатает в виде указателя (представление зависит от реализации). n int * ; число литер, напечатанных к данному моменту данным вызовом printf , записывается в аргумент. Никакие другие аргументы не преобразуются. % никакие аргументы не преобразуются; печатается % . printf(. ) полностью эквивалентна fprintf(stdout, . )
sprintf действует так же, как и printf , только вывод осуществляет в стринг s , завершая его литерой ‘\0’ . Стринг s должен быть достаточно большим, чтобы вмещать результат вывода. Возвращает количество записанных литер, в число которых литера ‘\0’ не входит.
Функции vprintf , vfprintf и vsprintf эквивалентны соответствующим printf -функциям с той лишь разницей, что переменный список аргументов представлен параметром arg , инициализированным макросом va_start и, возможно, вызовами va_arg . (См. в B7 описание <stdarg.h> .)
B1.3. Форматный ввод
Функции scanf имеют дело с форматным преобразованием при вводе.
fscanf читает данные из потока stream под управлением format и преобразованные величины присваивает по порядку аргументам, каждый из которых должен быть указателем. Завершает работу, если исчерпался формат. Выдает EOF по исчерпании файла или перед любым преобразованием, если возникла ошибка; в остальных случаях она возвращает количество преобразованных и введенных элементов.
Форматный стринг обычно содержит спецификации преобразования, которые используются для управления вводом. В форматный стринг могут входить:
- Пробелы и табуляции, которые игнорируются.
- Обычные литеры (но не % ), которые ожидаются в потоке ввода среди литер, отличных от пробельных.
- Спецификации преобразования, состоящие из % ; необязательного знака * , подавляющего присваивание; необязательного числа, специфицирующего максимальную ширину поля; необязательных h , l или L , указывающих размер присваиваемого значения, и литеры–спецификатора преобразования.
Спецификация преобразования определяет преобразование следующего поля ввода. Обычно результат размещается в переменной, на которую указывает соответствующий аргумент. Однако если присваивание подавляется при помощи * , как, например, в %*s , то поле ввода просто пропускается, и никакого присваивания не происходит. Поле ввода определяется как стринг непробельных литер; при этом ввод стринга прекращается при выполнении любого из двух условий: если встретилась пробельная литера или если ширина поля (в случае, когда она указана) исчерпана. Из этого следует, что при переходе к следующему полю scanf может «перешагивать» через границы строк, поскольку литера новая_строка является пробельной. (Под пробельными понимаются литеры пробела, табуляции, новой_строки, возврата_каретки, вертикальной_табуляции и смены_страницы.)
Литера-спецификатор указывает на способ интерпретации поля ввода. Соответствующий аргумент должен быть указателем. Список допустимых литер-спецификаторов приводится в табл. B.2.
Таблица B.2. Преобразования scanf
Литера Данные на вводе; тип аргумента d десятичное целое; int * . i целое; int * . Целое может быть восьмеричным (с ведущим нулем) или шестнадцатиричным (с ведущими 0x или 0X ). o восьмеричное целое (с ведущим нулем или без него); int * . u беззнаковое десятичное целое; unsigned int * . x шестнадцатиричное целое (с ведущим 0x или 0X или без); c литеры; char * . Литеры ввода размещаются в указанном массиве в количестве, заданном шириной поля; по умолчанию это количество равно 1. Литера ‘\0’ не добавляется. Пробельные литеры здесь рассматриваются как обычные литеры и поступают в аргумент. Чтобы прочесть следующую литеру, непробельную используйте %1s . s стринг непробельных литер (записывается без кавычек); char * , указывающий на массив размера достаточного, чтобы вместить стринг и добавляемую к нему литеру ‘\0’ . e , f , g число с плавающей точкой; float * . Формат ввода для float состоит из необязательного знака, стринга цифр, возможно, с десятичной точкой и необязательной экспоненты, состоящей из E или E и целого, возможно, со знаком. p значение указателя в виде, в котором printf("%p") его напечатает; void * . n записывает в аргумент число литер, прочитанных к этому моменту в этом вызове; int * . Никакого чтения ввода не происходит. Счетчик числа введенных элементов не увеличивается. [. ] выбирает из ввода самый длинный непустой стринг, состоящий из литер, заданных в квадратных скобках; char * . В конец стринга добавляется ‘\0’ . Спецификатор вида []. ] включает ] в задаваемое множество литер. [^. ] выбирает из ввода самый длинный непустой стринг, состоящий из литер, не входящих в заданное в скобках множество. В конец добавляется ‘\0’ . Спецификатор вида [^]. ] включает ] в задаваемое множество литер. % обычная литера % ; присваивание не делается. Литерам-спецификаторам d , i , n , o , u и x может предшествовать h , если аргумент есть указатель на short (а не int ) или l (буква эль), если аргумент есть указатель на long . Литерам-спецификаторам e , f и g может предшествовать l , если аргумент — указатель на double (а не float ), или L , если аргумент — указатель на long double .
scanf(. ) делает то же, что и fscanf(stdin, . ) .
sscanf(s. ) делает то же, что и scanf(. ) , только ввод литер осуществляет из стринга s .
B1.4. Функции ввода-вывода литер
fgetc возвращает следующую литеру из потока stream в виде unsigned char (переведенную в int ) или EOF , если исчерпан файл или обнаружена ошибка.
fgets читает не более n-1 литер в массив s , прекращая чтение, если встретилась литера новая_строка, которая включается в массив; кроме того, записывает в массив ‘\0’ . fgets возвращает s или, если исчерпан файл или обнаружена ошибка, NULL .
fputc пишет литеру c (переведенную в unsigned char ) в stream . Возвращает записанную литеру или EOF в случае ошибки.
fputs пишет стринг s (который может не иметь ‘\n’ ) в stream ; возвращает неотрицательное целое или EOF в случае ошибки.
getc делает то же, что и fgetc , но в отличие от последней, если она — макрос, stream может быть вычислен более одного раза.
getchar() делает то же, что getc(stdin) .
gets читает следующую строку ввода в массив s , заменяя литеру новая_строка на ‘\0’ . Возвращает s или, если исчерпан файл или обнаружена ошибка, NULL .
putc делает то же, что и fputc , но в отличие от последней, если она — макрос, stream может быть вычислен более одного раза.
putchar(c) делает то же, что putc(c, stdout) .
puts пишет стринг s и литеру новая_строка в stdout . Возвращает EOF , в случае ошибки, или неотрицательное значение, если запись прошла нормально.
ungetc отправляет литеру c (переведенную в unsigned char ) обратно в stream ; при следующем чтении из stream она будет получена снова. Для каждого потока вернуть можно не более одной литеры. Нельзя возвращать EOF . В качестве результата ungetc выдает отправленную назад литеру или, в случае ошибки, EOF .
B1.5. Функции прямого ввода-вывода
fread читает из потока stream в массив ptr не более nobj объектов размера size . Она возвращает количество прочитанных объектов, которое может быть меньше заявленного. Для индикации состояния после чтения следует использовать feof и ferror .
fwrite пишет из массива ptr в stream nobj объектов размера size ; возвращает число записанных объектов, которое в случае ошибки меньше nobj .
B1.6. Функции позиционирования файла
fseek устанавливает позицию для stream ; последующее чтение или запись будет производиться с этой позиции. В случае бинарного файла позиция устанавливается со смещением offset — относительно начала, если origin равен SEEK_SET; относительно текущей позиции, если origin равен SEEK_CUR; и относительно конца файла, если origin равен SEEK_END . Для текстового файла offset должен быть нулем или значением, полученным при помощи вызова функции ftell . При работе с текстовым файлом origin всегда должен быть равен SEEK_SET .
ftell возвращает текущую позицию потока stream или -1L , в случае ошибки.
rewind(fp) делает то же, что и fseek(fp, 0L, SEEK_SET); clearerr(fp) .
fgetpos записывает текущую позицию потока stream в *ptr для последующего использования ее в fsetpos . Тип fpos_t позволяет хранить такого рода значения. В случае ошибки fgetpos возвращает ненулевое значение.
fsetpos устанавливает позицию в stream , читая ее из *ptr , куда она была записана ранее при помощи fgetpos . В случае ошибки fsetpos возвращает ненулевое значение.
B1.7. Функции обработки ошибок
Многие функции библиотеки в случае ошибки или конца файла устанавливают индикаторы состояния. Эти индикаторы можно проверять и изменять. Кроме того, целое выражение errno (декларированное в <errno.h> ) может содержать номер ошибки, который дает дополнительную информацию о последней из обнаруженных ошибок.
clearerr очищает индикаторы конца файла и ошибки потока stream .
feof возвращает ненулевое значение, если для потока stream установлен индикатор конца файла.
ferror возвращает ненулевое значение, если для потока stream установлен индикатор ошибки.
perror(s) печатает s и реализационно-зависимое сообщение об ошибке, соответствующее целому значению в errno , т.е. делает то же, что и обращение к функции fprintf вида
B2. Проверки класса литеры: <ctype.h>
Головной файл <ctype.h> декларирует функции, предназначенные для проверок литер. Аргумент каждой из них имеет тип int и должен либо представлять собой EOF , либо быть значением unsigned char , приведенным к int ; возвращаемое значение тоже имеет тип int . Функции возвращают ненулевое значение («истина»), когда аргумент c удовлетворяет описанному условию или принадлежит указанному классу литер, и нуль в противном случае.
isalpha(c) или isdigit(c) есть истина isupper(c) или islower(c) есть истина управляющая литера десятичная цифра печатаемая литера кроме пробела буква нижнего регистра печатаемая литера, включая пробел печатаемая литера кроме пробела, буквы или цифры пробел, смена_страницы, новая_строка, возврат_каретки, табуляция, вертикальная_табуляция буква верхнего регистра шестнадцатиричная цифра В наборе семибитовых ASCII-литер печатаемые литеры находятся в диапазоне от 0x20 ( ‘ ‘ ) до 0x7E ( ‘
‘ ); управляющие литеры — от 0 ( NUL ) до 0x1F ( US ) и 0x7F ( DEL ).
Помимо перечисленных есть две функции, приводящие буквы к одному из регистров:
переводит c на нижний регистр переводит c на верхний регистр Если c — буква на верхнем регистре, то tolower(c) выдаст эту букву на нижнем регистре; в противном случае она вернет c . Если c — буква на нижнем регистре, то toupper(c) выдаст эту букву на верхнем регистре; в противном случае она вернет c .
B3. Функции, оперирующие со стрингами: <string.h>
Имеются две группы функций, оперирующих со стрингами. Они определены в головном файле <string.h> . Имена функций первой группы начинаются с str , второй — с mem . Если копирование имеет дело с объектами, перекрывающимися по памяти, то, за исключением memmove , поведение функций не определено. Функции сравнения рассматривают аргументы как массивы элементов типа unsigned char .
В следующей таблице переменные s и t принадлежат типу char * , cs и ct — типу const char * , n — типу size_t , а c — значение типа int , приведенное к типу char .
копирует стринг ct в стринг s , включая ‘\0’ ; возвращает s . копирует не более n литер стринга ct в s ; возвращает s . Дополняет результат литерами ‘\0’ , если литер в ct меньше n . конкатенирует ct к s ; возвращает s . конкатенирует не более n литер ct к s , завершая s литерой ‘\0’ ; возвращает s . сравнивает cs с ct ; возвращает <0 , если cs<ct , 0 , если cs==ct , и >0 , если cs>ct . сравнивает не более n литер cs и ct ; возвращает <0 , если cs<ct , 0 , если cs==ct , и >0 , если cs>ct . возвращает указатель на первое вхождение c в cs или, если такового не оказалось, NULL . возвращает указатель на последнее вхождение c в cs или, если такового не оказалось, NULL . возвращает длину начала cs , состоящего из литер, входящих в стринг ct . возвращает длину начала cs , состоящего из литер, не входящих в стринг ct . возвращает указатель в cs на первую литеру, которая совпала с одной из литер, входящих в ct , или, если такой не оказалось, NULL . возвращает указатель на первое вхождение ct в cs или, если такового не оказалось, NULL . возвращает длину cs . возвращает указатель на реализационно-зависимый стринг, соответствующий номеру ошибки n . strtok ищет в s лексему, ограниченную литерами из ct ; (более подробное описание этой функции см. ниже). Последовательные вызовы strtok разбивают стринг s на лексемы. Ограничителем лексемы может быть любая литера из числа входящих в ct . В первом вызове указатель s не есть NULL . Функция находит в стринге s первую лексему, состоящую из литер, не входящих в ct ; ее работа заканчивается тем, что поверх следующей литеры пишется ‘\0’ и возвращается указатель на лексему. Каждый последующий вызов, в котором указатель s равен NULL , выдает указатель на следующую лексему, которую функция будет искать сразу за концом предыдущей. Функция strtok возвращает NULL , если далее никакой лексемы не обнаружено. Параметр ct от вызова к вызову может варьироваться.
Функции mem. предназначены для манипулирования с объектами как с массивами литер; их назначение — получить интерфейсы к эффективным программам. В приведенной ниже таблице s и t принадлежат типу void * ; cs и ct — const void * ; n — size_t ; а c — значение типа int , приведенное к типу char .
копирует n литер из ct в s и возвращает s . делает то же самое, что и memcpy , но работает и в случае «перекрывающихся» объектов. сравнивает первые n литер cs и ct ; выдает тот же результат, что и функция strcmp . возвращает указатель на первое вхождение литеры c в cs или, если среди первых n литер c не встретилась, NULL . размещает литеру c в первых n позициях стринга s и возвращает s . B4. Математические функции: <math.h>
В головном файле <math.h> описываются математические функции и определяются макросы.
Макросы EDOM и ERANGE (находящиеся в <errno.h> ) задают отличные от нуля целочисленные константы, используемые для фиксации ошибки области и ошибки диапазона; HUGE_VAL определена как положительное double -значение. Ошибка области возникает, если аргумент выходит за область значений, для которой определена функция. Фиксация ошибки области осуществляется установкой EDOM в errno ; возвращаемое значение зависит от реализации. Ошибка диапазона возникает в том случае, когда результат функции не может быть представлен в виде double . В случае переполнения функция возвращает HUGE_VAL с правильным знаком и в errno устанавливает ERANGE . При исчезновении порядка функция возвращает нуль, а устанавливается ли в этом случае в errno ERANGE , зависит от реализации.
Далее x и y имеют тип double , n — тип int , и все функции возвращают значения типа double . Углы в тригонометрических функциях задаются в радианах.
синус x косинус x тангенс x арксинус x в диапазоне [-pi/2, pi/2] , x в [-1,1] арккосинус x в диапазоне [0, pi] , x в [-1, 1] арктангенс x в диапазоне [-pi/2, pi/2] арктангенс y/x в диапазоне [-pi, pi] гиперболический синус x гиперболический косинус x гиперболический тангенс x экспоненциальная функция e x натуральный логарифм ln(x) , x>0 десятичный логарифм log10(x) , x>0 x y . Ошибка области, если x=0 и y<0 или x<0 и y — не целое корень квадратный x , x>=0 наименьшее целое в виде double , которое >=x наибольшее целое в виде double , которое <=x абсолютное значение |x| x * 2 n разбивает x на два сомножителя, первый из которых — нормализованная дробь в интервале [1/2, 1] , которая возвращается, а второй — степень двойки, показатель которой запоминается в *exp . Если x — нуль, то обе части результата равны нулю. x разбивается на целую и дробную части, обе имеют тот же знак, что и x . Целая часть запоминается в *ip , дробная часть выдается как результат. остаток от деления x на y в виде числа с плавающей точкой. Знак результата совпадает со знаком x . При y==0 , результат зависит от реализации. B5. Функции общего назначения: <stdlib.h>
Головной файл <stdlib.h> декларирует функции, предназначенные для преобразования чисел, запроса памяти и других задач.
atof переводит s в double ; эквивалентна strtod(s, (char**) NULL) .
переводит s в int ; эквивалентна (int) strtol(s, (char**) NULL, 10) .
переводит s в long ; эквивалентна strtol(s, (char**) NULL, 10) .
strtod преобразует первые литеры s в double , игнорируя начальные пробельные литеры; запоминает указатель на непреобразованный конец в *endp (если endp не NULL ). В случае переполнения она выдает HUGE_VAL с соответствующим знаком, в случае исчезновения порядка — 0; в обоих случаях в errno устанавливается ERANGE .
strtol преобразует первые литеры s в long , игнорируя начальные пробельные литеры; запоминает указатель на непреобразованный конец в *endp (если endp не NULL ). Если base находится в диапазоне от 2 до 36, то преобразование делается в предположении, что на входе — запись числа по основанию base . Если base равен нулю, то основанием числа считается 8, 10 или 16; число, начинающееся с цифры 0, считается восьмеричным, а с 0x или 0X — шестнадцатиричным. Цифры от 10 до base-1 записываются начальными буквами латинского алфавита в любом регистре. В случае основания 16 в начале числа разрешается помещать 0x или 0X . При переполнении функция возвращает LONG_MAX или LONG_MIN (в зависимости от знака), а в errno устанавливается ERANCE .
strtoul работает так же, как и strtol , с той только разницей, что выдает результат типа unsigned long , а в случае переполнения — ULONG_MAX .
rand выдает псевдослучайное число в диапазоне от 0 до RAND_MAX ; RAND_MAX не меньше 32767.
srand использует seed в качестве «затравки» для новой последовательности псевдослучайных чисел. Изначально параметр seed равен 1.
calloc возвращает указатель на место в памяти, отведенное для массива nobj объектов, каждый из которых размера size , или, если памяти запрашиваемого объема нет, NULL . Выделенная область памяти обнуляется.
malloc возвращает указатель на место в памяти для объекта размера size или, если памяти запрашиваемого объема нет, NULL . Выделенная область памяти не инициализируется.
realloc заменяет размер объекта, на который указывает p , на size . Для части, размер которой равен наименьшему из старого и нового размеров, содержимое не изменяется. Если новый размер больше старого, дополнительное пространство не инициализируется. realloc возвращает указатель на новое место памяти или, если требования не могут быть удовлетворены, NULL ( *p при этом не изменяется).
free освобождает область памяти, на которую указывает p ; эта функция ничего не делает, если p есть NULL . В p должен стоять указатель на область памяти, ранее выделенную одной из функций: calloc , malloc или realloc .
abort вызывает аварийное завершение программы, ее действия эквивалентны вызову raise(SIGABRT) .
exit вызывает нормальное завершение программы. Функции, зарегистрированные при помощи atexit , выполняются в порядке, обратном их регистрации. Производится выталкивание буферов открытых файлов, открытые потоки закрываются, и управление возвращается в среду, из которой был произведен запуск программы. Значение status , передаваемое в среду, зависит от реализации, однако при успешном завершении программы это всегда нуль. Можно также использовать значения EXIT_SUCCESS (в случае успешного завершения) и EXIT_FAILURE (в случае ошибки).
atexit регистрирует fcn в качестве функции, которая будет вызываться при нормальном завершении программы; возвращает ненулевое значение, если регистрация не может быть сделана.
system передает стринг s операционной среде для выполнения. Если s есть NULL и существует командный процессор, то system возвращает ненулевое значение. Если s не NULL , то возвращаемое значение зависит от реализации.
getenv возвращает стринг среды, связанный с name , или, если никакого стринга не существует, NULL . Детали зависят от реализации.
bsearch среди base[0]. base[n-1] ищет элемент с подходящим ключом *key . Функция cmp сравнивает первый аргумент (ключ поиска) со своим вторым аргументом (значением ключа в таблице) и в зависимости от результата сравнения выдает отрицательное число, нуль или положительное значение. Элементы массива base должны быть упорядочены в возрастающем порядке. bsearch возвращает указатель на элемент с подходящим ключом или, если такого не оказалось, NULL .
qsort сортирует массив base[0]. base[n-1] объектов размера size в возрастающем порядке. Функция сравнения cmp — такая же, что и в bsearch .
abs возвращает абсолютное значение int -аргумента.
labs возвращает абсолютное значение long -аргумента.
div вычисляет частное и остаток от деления num на denom . Результаты запоминаются в int -членах quot и rem структуры div_t .
ldiv вычисляет частное и остаток от деления num на denom . Результаты запоминаются в long -членах quot и rem структуры ldiv_t .
B6. Диагностика: <assert.h>
Макрос assert используется для включения в программу диагностических сообщений.
Если выражение есть нуль, то
напечатает в stderr сообщение следующего вида:
после чего будет вызвана функция abort , которая завершит вычисления. Имя исходного файла и номер строки будут взяты из макросов __FILE__ и __LINE__ .
Если в момент включения файла <assert.h> было определено имя NDEBUG , то макрос assert игнорируется.
B7. Списки аргументов переменной длины: <stdarg.h>
Головной файл <stdarg.h> предоставляет средства для перебора аргументов функции, количество и типы которых заранее не известны.
Пусть посларг — последний именованный параметр функции f с переменным числом аргументов. Внутри f декларируется переменная ap типа va_list , предназначенная для хранения указателя на очередной аргумент:
Прежде чем будет возможен доступ к безымянным аргументам, необходимо один раз инициализировать ap , обратившись к макросу va_start :
С этого момента каждое обращение к макросу:
будет давать значение очередного безымянного аргумента указанного типа, и каждое такое обращение будет вызывать автоматическое продвижение указателя ap , чтобы последний был готов к выдаче следующего аргумента. Один раз после перебора аргументов, но до выхода из f необходимо обратиться к макросу
B8. Далекие переходы: <setjmp.h>
Декларации в <setjmp.h> предоставляют способ отклониться от обычной последовательности «вызов–возврат»; типичная ситуация — необходимость вернуться из «глубоко вложенного» вызова функции на верхний уровень, минуя промежуточные возвраты.
Макрос setjmp сохраняет текущую информацию о вызовах в env для последующего ее использования в longjmp . Возвращает нуль, если возврат осуществляется непосредственно из setjmp , и не нуль, если — от последующего вызова longjmp . Обращение к setjmp возможно только в определенных контекстах; в основном это проверки в if , switch и циклах, причем только в простых выражениях отношения.
longjmp восстанавливает информацию, сохраненную в самом последнем вызове setjmp , по информации из env ; счет возобновляется, как если бы функция setjmp только что отработала и вернула ненулевое значение val . Результат будет непредсказуемым, если в момент обращения к longjmp функция, содержащая вызов setjmp , уже «отработала» и осуществила возврат. Доступные ей объекты имеют те значения, которые они имели в момент обращения к longjmp ; setjmp не сохраняет значений.
B9. Сигналы: <signal.h>
Головной файл <signal.h> предоставляет средства для обработки исключительных ситуаций, возникающих во время выполнения программы, таких, как прерывание, вызванное внешним источником или ошибкой в вычислениях.
signal устанавливает, как будут обрабатываться последующие сигналы. Если параметр handler имеет значение SIG_DFL , то используется реализационно-зависимая «обработка по умолчанию»; если значение handler равно SIC_ICN , то сигнал игнорируется; в остальных случаях будет выполнено обращение к функции, на которую указывает handler с типом сигнала в качестве аргумента. В число допустимых видов сигналов входят:
SIGABRT аварийное завершение, например, от abort SIGFPE арифметическая ошибка: деление на 0 или переполнение SIGILL неверный код функции (недопустимая команда) SIGINT запрос на взаимодействие, например, прерывание SIGSEGV неверный доступ к памяти, например, выход за границы SIGTERM требование завершения, посланное в программу signal возвращает предыдущее значение handler — в случае специфицированного сигнала, или SICERR — в случае возникновения ошибки.
Когда в дальнейшем появляется сигнал sig , сначала восстанавливается готовность поведения «по умолчанию», после чего вызывается функция, заданная в параметре handler , т.е. как бы выполняется вызов (*handler) (sig) . Если handler -функция вернет назад управление, то вычисления возобновятся с того места, в каком застал программу пришедший сигнал.
Начальное состояние сигналов зависит от реализации.
raise посылает сигнал sig в программу. В случае неудачи возвращает ненулевое значение.
B10. Функции даты и времени: <time.h>
Головной файл <time.h> декларирует типы и функции, связанные с датой и временем. Некоторые функции имеют дело с местным временем, которое может отличаться от календарного, например, в связи с зонированием времени. Типы clock_t и time_t — арифметические типы для представления времени, а struct tm содержит компоненты календарного времени:
секунды от начала минуты (0, 61) минуты от начала часа (0, 59) часы от полуночи (0, 23) число месяца (1, 31) месяцы после января (0, 11) годы после 1900 дни после воскресенья (0, 6) дни после 1 января (0, 365) признак светлого времени Значение tm_isdst — положительное, если время приходится на светлую часть суток, нуль в противном случае и отрицательное, если информация недоступна.
clock возвращает время, фиксируемое процессором от начала счета программы, или -1, если оно не известно. Для выражения этого времени в секундах применяется формула clock()/CLOCKS_PER_SEC .
time возвращает текущее календарное время или -1, если время не известно. Если tp не NULL , то возвращаемое значение записывается и в *tp .
difftime возвращает разность time2-time1 , выраженную в секундах.
mktime преобразует местное время, заданное структурой *tp , в календарное, выдавая его в том же виде, что и функция time . Компоненты будут иметь значения в указанных диапазонах. Функция mktime возвращает календарное время или -1, если оно не представимо.
Следующие четыре функции возвращают указатели на статические объекты, каждый из которых может быть изменен другими вызовами.
asctime переводит время в структуре *tp в стринг вида
ctime переводит календарное время в местное, что эквивалентно выполнению
gmtime переводит календарное время во Всемирное координированное время (Coordinated Universal Time — UTC). Выдает NULL , если UTC не известно. Имя этой функции, gmtime , происходит от Greenwich Mean Time (среднее время по Гринвичскому меридиану).
localtime переводит календарное время *tp в местное.
strftime форматирует информацию о дате и времени из *tp в стринг s согласно формату fmt , который имеет много общих черт с форматом, задаваемым в функции printf . Обычные литеры (включая и завершающую литеру ‘\0’ ) копируются в s . Каждая пара, состоящая из % и буквы, заменяется, как показано ниже, с использованием значений по форме, соответствующей локальной среде. В s размещается не более smax литер. strftime возвращает число литер без учета ‘\0’ или нуль, если число сгенерированных литер больше smax .
Библиотеки Си
Вы, наверное, уже задались вопросом, что означает первая строка программы на Cи:
Какова её роль и можно ли обойтись без неё? Строка #include делает очень важную вещь: она подключает библиотеки уже написанного кода к вашей программе.
Имя подключенной библиотеки идет в угловых скобках (<>) и носит расширение (.h) . Если бы не было библиотек, то любое, самое элементарное действие, пришлось бы каждый раз описывать снова и снова. Подключенная нами библиотека содержит функции ввода/вывода. Именно она позволяет нам использовать функцию printf() для вывода на экран. То есть, если бы мы не написали строку #include <stdio.h> , но оставили в теле программы функцию printf (), при попытке запуска мы бы получили ошибку! Потому что без этой библиотеки компилятор не знает, что такое printf() .
Есть библиотеки стандартные, они составляют словарный запас языка. Функция printf() не встроена в компьютер, но входит в стандартную библиотеку языка Cи. То есть, некий программист ранее написал её и включил в библиотеку. Теперь другие могут ею пользоваться, не изобретая велосипед. Чтобы компилятор её «понял», подключаем <stdio.h> .
Есть и другие стандартные библиотеки, используемые в процессе прохождения CS50. Например, библиотека строк string.h, где описаны операции со строками (определение длины, сложение и прочее).
По сравнению с другими популярными языками программирования, количество стандартных библиотек Cи очень невелико. Но есть самописные, чаще всего — более узкоспециализированные библиотеки. Так, библиотека cs50.h была создана специально для студентов CS50.
Самое время сделать важное замечание: помимо написания программ, решения задач с помощью собственного кода, хороший разработчик обладает еще одним важным навыком: знанием инструментов, уже написанных и умением использовать их (чужие библиотеки), чтобы не тратить время на изобретение «колеса».
Так, если вы находитесь в процессе решения нудной или сложной задачи, которая при этом кажется довольно распространенной, привыкайте задавать себе вопрос: «а не написал ли её решение кто-нибудь другой?» Велики шансы, что так оно и есть, и вы можете найти эту функцию в существующей библиотеке.
В технических терминах, библиотека — это двоичный файл, полученный путем объединения в коллекцию объектных файлов, используя компоновщик. Объектные файлы — это те файлы с расширением (*.o) , которые вы получаете при компиляции приложений.
Стандартная библиотека C
Стандартная библиотека C или Libc является стандартной библиотекой для языка программирования C , как указана в ISO C стандарте. [1] Начиная с исходного стандарта ANSI C , он был разработан одновременно со спецификацией POSIX библиотеки C , которая является его надмножеством. [2] [3] Так как ANSI C был принят Международной организацией по стандартизации , [4] С стандартная библиотека также называется библиотека ISO C .
Стандартная библиотека C предоставляет макросы , определения типов и функции для таких задач, как обработка строк , математические вычисления, обработка ввода / вывода, управление памятью и некоторые другие службы операционной системы .
Интерфейс прикладного программирования
Заголовочные файлы
Интерфейс прикладного программирования (API) стандартной библиотеки C объявлен в ряде файлов заголовков . Каждый файл заголовка содержит одно или несколько объявлений функций, определений типов данных и макросов.
После длительного периода стабильности, три новых файлы заголовки ( iso646.h , wchar.h и wctype.h ) были добавлены с Нормативным Добавлением 1 (NA1), в дополнении к C стандарту ратифицированного в 1995 году еще шесть заголовков файлы ( complex.h , fenv.h , inttypes.h , stdbool.h , stdint.h , и tgmath.h ) были добавлены с C99 , пересмотром в C стандарт , опубликованный в 1999 году, и более пяти файлов ( stdalign.h , stdatomic.h , stdnoreturn.h , threads.h , и uchar.h ) с С11 в 2011 г. в общей сложности, в настоящее время существует 29 файлов заголовки:
Имя Из Описание <assert.h> Содержит макрос assert , используемый для помощи в обнаружении логических ошибок и других типов ошибок при отладке версий программы. <complex.h> C99 Набор функций для работы с комплексными числами . <ctype.h> Определяет набор функций, используемых для классификации символов по их типам или для преобразования между верхним и нижним регистром способом, который не зависит от используемого набора символов (обычно ASCII или одно из его расширений, хотя также известны реализации, использующие EBCDIC ). <errno.h> Для тестирования кодов ошибок, сообщаемых библиотечными функциями. <fenv.h> C99 Определяет набор функций для управления средой с плавающей запятой . <float.h> Определяет макроконстанты, определяющие зависящие от реализации свойства библиотеки с плавающей запятой . <inttypes.h> C99 Определяет целочисленные типы точной ширины . <iso646.h> NA1 Определяет несколько макросов , реализующих альтернативные способы выражения нескольких стандартных токенов. Для программирования в наборах символов варианта ISO 646 . <limits.h> Определяет макроконстанты, определяющие зависящие от реализации свойства целочисленных типов. <locale.h> Определяет функции локализации . <math.h> Определяет общие математические функции . <setjmp.h> Объявляет макросы setjmp и longjmp , которые используются для нелокальных выходов. <signal.h> Определяет функции обработки сигналов . <stdalign.h> C11 Для запроса и указания выравнивания объектов. <stdarg.h> Для доступа к разному количеству аргументов, передаваемых функциям. <stdatomic.h> C11 Для атомарных операций с данными, совместно используемыми потоками. <stdbool.h> C99 Определяет логический тип данных . <stddef.h> Определяет несколько полезных типов и макросов . <stdint.h> C99 Определяет целочисленные типы точной ширины . <stdio.h> Определяет основные функции ввода и вывода <stdlib.h> Определяет функции числового преобразования , функции числа генерации псевдослучайных , распределение памяти , функции управления процессом <stdnoreturn.h> C11 Для указания невозвратных функций <string.h> Определяет функции обработки строк <tgmath.h> C99 Определяет типовые математические функции . <threads.h> C11 Определяет функции для управления несколькими потоками , мьютексами и условными переменными <time.h> Определяет функции обработки даты и времени <uchar.h> C11 Типы и функции для манипулирования Unicode символов <wchar.h> NA1 Определяет функции обработки широких строк <wctype.h> NA1 Определяет набор функций, используемых для классификации широких символов по их типам или для преобразования между верхним и нижним регистром. Три файла заголовков ( complex.h , stdatomic.h и threads.h ) являются условными функциями, поддержка которых не требуется.
Стандарт POSIX добавил несколько нестандартных заголовков C для специфичных для Unix функций. Многие нашли свой путь к другим архитектурам. Примеры включают fcntl.h и unistd.h . Ряд других групп используют другие нестандартные заголовки — это есть в библиотеке GNU C alloca.h , а в HP OpenVMS есть va_count() функция.
Документация
В Unix-подобных системах авторитетная документация фактически реализованного API предоставляется в виде страниц руководства . В большинстве систем справочные страницы стандартных библиотечных функций находятся в разделе 3; Раздел 7 может содержать несколько общих страниц, посвященных базовым концепциям (например, man 7 math_error в Linux ).
Реализации
Unix-подобные системы обычно имеют библиотеку C в форме общей библиотеки , но файлы заголовков (и набор инструментов компилятора) могут отсутствовать при установке, поэтому разработка на C может быть невозможна. Библиотека C считается частью операционной системы в Unix-подобных системах. Функции C, включая стандартные функции ISO C, широко используются программами и считаются не только реализацией чего-то на языке C, но и де-факто частью интерфейса операционной системы. Unix-подобные операционные системы обычно не могут работать, если библиотека C. Это верно для приложений, которые связаны динамически, а не статически. Кроме того, само ядро (по крайней мере, в случае Linux) работает независимо от каких-либо библиотек.
В Microsoft Windows основные системные динамические библиотеки ( DLL ) обеспечивают реализацию стандартной библиотеки C для компилятора Microsoft Visual C ++ v6.0; стандартная библиотека C для новых версий компилятора Microsoft Visual C ++ предоставляется каждым компилятором индивидуально, а также распространяемыми пакетами. Скомпилированные приложения, написанные на C, либо статически связаны с библиотекой C, либо связаны с динамической версией библиотеки, которая поставляется с этими приложениями, вместо того, чтобы полагаться на присутствие в целевых системах. Функции библиотеки C компилятора не рассматриваются как интерфейсы для Microsoft Windows.
Существует множество других реализаций, поставляемых как с различными операционными системами, так и с компиляторами C. Вот некоторые из популярных реализаций:
-
BSD Libc , различные реализации распределенных с BSD -derived операционные системы (glibc), используемая в GNU Hurd , GNU / kFreeBSD и Linux , часть Microsoft Visual C ++ , альтернативная небольшая реализация стандартной библиотеки C (без MMU) , стандартная библиотека C для встроенных систем μClinux (без MMU)
-
, встроенная библиотека C, форк μClibc, все еще поддерживается, с поддержкой блока управления памятью (MMU)
Встроенные функции компилятора
Некоторые компиляторы (например, GCC [7] ) предоставляют встроенные версии многих функций стандартной библиотеки C; то есть реализации функций записываются в скомпилированный объектный файл , и программа вызывает встроенные версии вместо функций из общего объектного файла библиотеки C. Это снижает накладные расходы на вызов функций, особенно если вызовы функций заменяются встроенными вариантами, и позволяет использовать другие формы оптимизации (поскольку компилятор знает характеристики потока управления встроенных вариантов), но может вызвать путаницу при отладке (например, , встроенные версии нельзя заменить инструментальными варианты).
Однако встроенные функции должны вести себя как обычные функции в соответствии с ISO C. Основное значение состоит в том, что программа должна иметь возможность создавать указатель на эти функции, принимая их адрес, и вызывать функцию с помощью этого указателя. Если два указателя на одну и ту же функцию получены в двух разных единицах трансляции в программе, эти два указателя должны сравниваться одинаково; то есть адрес приходит путем разрешения имени функции, которая имеет внешнюю (программную) связь.
Связывание, libm
В FreeBSD [8] и glibc, [9] некоторые функции, такие как sin (), не связаны по умолчанию и вместо этого включены в математическую библиотеку libm . Если какой-либо из них используется, компоновщику должна быть предоставлена директива -lm . POSIX требует, чтобы компилятор c99 поддерживал -lm и чтобы функции, объявленные в заголовках math.h , complex.h и fenv.h были доступны для связывания, если -lm указано, но не указывает, связаны ли функции по умолчанию. [10] musl удовлетворяет этому требованию, помещая все в одну библиотеку libc и предоставляя пустую библиотеку libm. [11]
Обнаружение
Согласно стандарту C макрос __STDC_HOSTED__ должен иметь значение 1, если реализация размещена. Размещенная реализация имеет все заголовки, указанные в стандарте C. Реализация также может быть автономной, что означает, что эти заголовки не будут присутствовать. Если реализация отдельно стоящая , она должна определить __STDC_HOSTED__ до 0 .
Проблемы и обходные пути
Уязвимости переполнения буфера
Некоторые функции в стандартной библиотеке C были печально известны уязвимостью переполнения буфера и в целом поощряли ошибочное программирование с момента их принятия. [a] Наиболее критикуемыми объектами являются:
-
, включая strcpy() и strcat() , из-за отсутствия проверки границ и возможных переполнений буфера, если границы не проверяются вручную;
- строковые подпрограммы в целом, для побочных эффектов , поощряющие безответственное использование буфера, не всегда гарантирующие корректный вывод с завершающим нулем , вычисление линейной длины; [b]
- printf() семейство подпрограмм для испорчения стека выполнения, когда строка формата не соответствует заданным аргументам. Этот фундаментальный недостаток создал целый класс атак: атаки на строку формата ;
- gets() и scanf() семейство подпрограмм ввода-вывода из-за отсутствия (любой или простой) проверки длины ввода.
За исключением крайнего случая gets() , всех уязвимостей безопасности можно избежать, введя вспомогательный код для управления памятью, проверки границ, проверки ввода и т. Д. Это часто делается в форме оболочек, которые делают стандартные библиотечные функции более безопасными и простыми в использовании. Это восходит к книге Б. Кернигана и Р. Пайка «Практика программирования», авторы которой обычно используют оболочки, которые выводят сообщения об ошибках и закрывают программу в случае возникновения ошибки.
Комитет ISO C опубликовал технические отчеты TR 24731-1 [12] и работает над TR 24731-2 [13], чтобы предложить принятие некоторых функций с проверкой границ и автоматическим распределением буфера, соответственно. Первый был встречен резкой критикой и некоторой похвалой [14] [15], второй получил неоднозначную реакцию. Несмотря на это, TR 24731-1 был реализован в стандартной библиотеке Microsoft C, и его компилятор выдает предупреждения при использовании старых «небезопасных» функций.
Проблемы с потоками, уязвимость к условиям гонки
strerror() Рутина критикуют за то , что поток небезопасным и иначе уязвимы для условий гонки .
Обработка ошибок
Обработка ошибок функций в стандартной библиотеке C непоследовательна и иногда сбивает с толку. Согласно странице руководства Linux math_error : «Текущая (версия 2.8) ситуация с glibc запутана. Большинство (но не все) функций вызывают исключения при ошибках. Некоторые также устанавливают errno . Некоторые функции устанавливают errno , но не вызывают исключения. . Очень немногие функции не делают ни того, ни другого. » [16]
Стандартизация
Исходный язык C не предоставлял встроенных функций, таких как операции ввода-вывода, в отличие от традиционных языков, таких как COBOL и Fortran . [ необходима цитата ] Со временем сообщества пользователей C делились идеями и реализациями того, что сейчас называется стандартными библиотеками C. Многие из этих идей со временем были включены в определение стандартизованного языка C.
И Unix, и C были созданы в Bell Laboratories AT&T в конце 1960-х — начале 1970-х годов. В течение 1970-х годов язык Си становился все более популярным. Многие университеты и организации начали создавать свои собственные варианты языка для своих собственных проектов. К началу 80-х годов прошлого века проблемы совместимости между различными реализациями языка Си стали очевидны. В 1983 году Американский национальный институт стандартов (ANSI) сформировал комитет, чтобы установить стандартную спецификацию языка C, известную как « ANSI C ». Эта работа завершилась созданием так называемого стандарта C89 в 1989 году. Частью получившегося стандарта стал набор программных библиотек, названный стандартной библиотекой ANSI C.
Стандартная библиотека POSIX
POSIX , а также SUS , определяют ряд подпрограмм, которые должны быть доступны помимо подпрограмм в базовой стандартной библиотеке C. Спецификация POSIX включает файлы заголовков, среди прочего, для многопоточности , работы в сети и регулярных выражений . Они часто реализуются вместе с функциональными возможностями стандартной библиотеки C с разной степенью близости. Например, glibc реализует такие функции, как fork внутри libc.so , но до NPTL был объединен с glibc, он составлял отдельную библиотеку со своим собственным аргументом флага компоновщика. Часто эта функциональность, указанная в POSIX, рассматривается как часть библиотеки; базовая библиотека C может быть идентифицирована как библиотека C ANSI или ISO .
BSD libc
BSD libc — это расширенный набор стандартной библиотеки POSIX, поддерживаемой библиотеками C, включенными в операционные системы BSD, такие как FreeBSD , NetBSD , OpenBSD и macOS . BSD libc имеет некоторые расширения, которые не определены в исходном стандарте, многие из которых впервые появились в выпуске 4.4BSD 1994 года (первое, которое в значительной степени было разработано после того, как первый стандарт был выпущен в 1989 году). Некоторые из расширений BSD libc:
- sys/tree.h — содержит реализацию красно-черного дерева и расширенного дерева[17][18]
- sys/queue.h — реализации связанного списка , очередей , хвостовой очереди и т. Д. [19][20]
- fgetln() — определено в stdio.h . Это можно использовать для чтения файла построчно. [21][22][23]
- fts.h — содержит некоторые функции для обхода файловой иерархии [24][25]
- db.h — некоторые функции для подключения к Berkeley DB[26][27]
- strlcat() и strlcpy() — безопасные альтернативы для strncat() и strncpy() [28][29][30][31][32]
- err.h — содержит некоторые функции для печати форматированных сообщений об ошибках [33][34]
- vis.h — содержит vis() функцию. Эта функция используется для отображения непечатаемых символов в визуальном формате. [35][36][37]
Стандартная библиотека C на других языках
Некоторые языки включают функциональные возможности стандартной библиотеки C в свои собственные библиотеки. Библиотека может быть адаптирована для лучшего соответствия структуре языка, но операционная семантика остается аналогичной. В C ++ язык, например, включает в себя функциональность стандартной библиотеки C в пространстве имен std (например, std::printf , std::atoi , std::feof ), в заголовочных файлах с похожими именами тех , С ( cstdio , cmath , cstdlib и т.д.). Другие языки, использующие аналогичные подходы, — это D , Perl , Ruby и основная реализация Python, известная как CPython. . В Python 2, например, встроенные файловые объекты определены как «реализованные с использованием stdio пакета C » [38], поэтому ожидается, что доступные операции (открытие, чтение, запись и т. Д.) Будут иметь такое же поведение, как и соответствующие функции C. В Rust есть ящик под названием libc, который позволяет использовать несколько функций C, структур и других определений типов. [39]
Сравнение со стандартными библиотеками других языков
Стандартная библиотека C мала по сравнению со стандартными библиотеками некоторых других языков. Библиотека C предоставляет базовый набор математических функций, операций со строками, преобразования типов , а также файлового и консольного ввода-вывода. Он не включает стандартный набор « типов контейнеров », таких как Стандартная библиотека шаблонов C ++ , не говоря уже о полных наборах инструментов графического пользовательского интерфейса (GUI), сетевых инструментах и множестве других функций, которые Java и .NET Framework предоставить в стандартной комплектации. Основное преимущество небольшой стандартной библиотеки заключается в том, что создание рабочей среды ISO C намного проще, чем с другими языками, и, следовательно, перенос C на новую платформу сравнительно прост.