Стандартная библиотека libc Введение: что такое библиотека? Библиотека – набор объектных файлов объединенных в один файл Заголовки используемых функций включаются в программу с помощью директивы #include Когда программа использует библиотечную функцию, компоновщик находит ее и добавляет в программу Подключение библиотеки gcc prog.c −lm −lm – ключ транслятора (−l) и «короткое имя» библиотеки: (m) полное имя – libm.a, а короткое строится по правилам: lib + короткое имя + {.a или .so} можно подключить библиотеку указав полный путь: cc prog.c /usr/lib/libm.a Обзор стандартной библиотеки C Стандартная библиотека C (libc) имеет 24 заголовочных файла файл <assert.h> <ctype.h> <errno.h> <float.h> <limits.h> <locate.h> <math.h> <setjmp.h> <signal.h> <stdarg.h> <stddef.h> <stdio.h> <stdlib.h> <string.h> <time.h> описание определяет макрос assert() – проверка условия функции классификации символов идентификация ошибок и сообщения об ошибках параметры типов с плавающей точкой параметры переменных целых типов функции связанные с локализацией элементарные математические функции нелокальные (дальние) переходы обработка сигналов функции с произвольным числом аргументов часто используемые константы и типы данных система ввода/вывода функции общего назначения функции обработки строк (C-strings) работа с временем и датой Библиотека файлового ввода-вывода (file IO) Порядок работы с файлами 1 Файл «открывается»: создаётся универсальное устройство ввода-вывода называемое поток (stream) 2 Производится обмен информацией между программой и файлом: с точки зрения программы происходит чтение данных из потока (или запись в поток) 3 Файл «закрывается»; при нормальном завершении программы main() (или exit()) все открытые файлы закрываются; при аварийном завершении (или вызове abort()) файлы не закрываются и информация может быть потеряна В языке С файлом может быть все что угодно, начиная с дискового файла и заканчивая терминалом или принтером. Потоки Бинарный поток – поток данных «как есть», без изменений Текстовой поток – поток символов собранных в «строки» (”\n“ в конце строки); в текстовом потоке может происходить преобразование некоторых символов При работе с файлами используют указатель на структуру типа FILE Указатель на файл #include <stdio.h> FILE * fp; Структура FILE содержит информацию как о файле так и о связанном с ним потоке Внимание Пользуйтесь только указателями Никогда ничего не меняйте в этой структуре Стандартные потоки (создаются автоматически): extern FILE * stdin; extern FILE * stdout; extern FILE * stderr; Text terminal Display Keyboard std out stderr n stdi Program Концепция буферизации Небуферизованный поток – запись/чтение производится символ за символом Построчная буферизация – запись/чтение в буфер, передача из буфера по приходу символа перевода строки Полная буферизация – запись/чтение в файл тогда, когда буфер полностью заполнен; размер буфера выбирается исходя из задачи Стандартные потоки: stdin – всегда буферизован stderr – всегда небуферизован stdout – поток буферизован построчно если вывод на терминал, иначе полная буферизация Открытие файла: fopen() прототип функции определен в <stdio.h>: FILE * fopen(const char* fname, const char* mode); открывает файл, имя которого задается параметром fname и связывает с ним поток параметр mode задает режим в котором файл будет открыт: mode действие r открыть текстовый файл для чтения w создать текстовый файл для записи, а если файл уже существует переписать a + дописать в конец текстового файла открыть файл для обновления (для чтения и записи); ставится после r,w,a создать бинарный поток; ставится после r,w,a или r+,w+,a+ b Закрытие файла: fclose() Прототип функции: int fclose(FILE * fp); fclose() закрывает поток; fp – указатель полученный от ранее вызванного fopen() все данные из буфера записываются в файл в случае успеха возвращает ноль при ошибке возвращает EOF (end-of-file); причину ошибки можно выяснить с помощью функции ferror() Внимание Рекомендуется вызывать fclose() в явном виде, так как количество одновременно открытых файлов в системе ограниченно Чтение-запись одного символа: getc(), putc() Прототипы: int getc(FILE* fp); int putc(int ch, FILE* fp); /* Чтение символа */ /* Запись символа */ fp – указатель полученный от fopen() ch – записываемый символ (putc) обе функции возвращают целое значение считанного/записанного символа в случае ошибки возвращают EOF getc() возвращает EOF по достижению конца файла (более точную информацию даст функция feof()) putchar(c) то же самое, что и putc(c,stdout) getchar(c) то же самое, что и getc(stdin) Пример: вывод содержимого файла на экран начало . . . #include <stdio.h> #include <stdlib.h> . . . продолжение int main(int argc, char *argv[]) { FILE *fp; char ch; if( argc != 2 ) { printf("No file name.\n"); exit(1); } fp=fopen(argv[1], "r"); if( fp == NULL ) { printf("Error open file\n"); exit(1); } ch = getc(fp); while( ch != EOF ) { putchar(ch); ch = getc(fp); } fclose(fp); } return 0; Проверка на ошибки Внешняя переменная errno #include <errno.h> /* define volatile int errno; */ Переменная errno содержит: в начале работы программы ноль если при вызове библиотечной функции произошла ошибка – номер ошибки Функция strerror() #include <string.h> char * strerror(int errno); функция возвращает строку, содержащую системное сообщение об ошибке, связанной со значением errno Функция perror() #include <stdio.h> void perror(const char* str); функция преобразует errno в строку и записывает её в stderr str – дополнительное сообщение printf(" initial value of errno = %d\n",errno); double a = -1.2; double b = sqrt(a); if( errno != 0 ) { printf(" after sqrt() errno = %d\n",errno); printf(" strerror msg: %s\n",strerror(errno)); perror(" perror msg"); } initial value of errno = 0 after sqrt() errno = 33 strerror msg: Numerical argument out of domain perror msg: Numerical argument out of domain Работа с ошибками ввода-вывода int ferror(FILE * stream) возвращает ненулевое значение (true) если в stream произошла ошибка Пример: if( ferror(fp) ) perror("Error in file"); int feof(FlLE * stream) возвращает ненулевое значение (true) если дошли до конца stream void clearerr(FILE * stream) обнуляет индикатор конца файла и индикатор ошибок для потока stream Функции ввода-вывода Функции аналогичные функциям printf() и scanf(): Xформатный ввод-вывод в файл int fprintf(FILE *stream, const char format, ...) int fscanf(FILE *stream, const char format, ...) Xформатный ввод-вывод в С-string int sprintf(char *buf, const char *format, ...); int sscanf(const char *buf, const char *format, ...); Ввод-вывод блоков данных (неформатированный) size_t fread(void *ptr, size_t size, size_t nobj, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *stream); ptr – указатель на область памяти (например массив), которая записывается (fwrite()) или считывается (fread()) из файла nobj – число элементов в буфере size – размер одного элемента в байтах Обе функции возвращают количество прочитанных/записанных элементов size_t – беззнаковый целый тип (оператор sizeof возвращает именно этот тип); применяется для задания размера объекта. Печать: printf("%zu\n",sizeof(int) ); Пример: запись бинарных данных в файл #include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; double d = 12.23; int i = 101; long l = 123023L; if( (fp=fopen("test.dat", "wb+")) == NULL ) { perror(" Error open file"); exit(1); } fwrite(&d, sizeof(double), 1, fp); fwrite(&i, sizeof(int), 1, fp); fwrite(&l, sizeof(long), 1, fp); . . . и их последующее чтение. rewind(fp); // rewind the ’fp’ back to its beginning fread(&d, sizeof(double), 1, fp); fread(&i, sizeof(int), 1, fp); fread(&l, sizeof(long), 1, fp); printf("%f %d %ld\n", d, i, l); fclose(fp); } return 0; Output: 12.230000 101 123023 Перенаправление потоков stdout → файл: B ./a.out > output.dat ⇒ вывод программы записать в файл output.dat B ./a.out >>output.dat ⇒ дописать в конец файла файл → stdin B ./a.out < input.dat ⇒ чтение из файла input.dat чтение из файла input.dat и вывод в файл output.dat: B ./a.out < input.dat > output.dat stdout + stderr → файл B ./a.out > output.dat 2 > &1 B ./a.out >& output.dat # POSIX # simplified non-POSIX Конвейер (pipe) B program1 | program2 вывод программы-1 перенаправляется на вход программы-2: stdout(program1) → stdin(program2) Цепочка: B Program1 | Program2 | Program3 stdout3 3 err d t Text terminal s Display Keyboard stderr2 std err 1 Program 3 stdout2 ⇒ stdin3 Program 2 stdout1 ⇒ stdin2 Program 1 stdin1