Ответ:
Бинарный (двоичный) поток:
· Последовательность произвольных байтов
· Не рассчитан на интерпретацию в качестве последовательности текстовых символов;
· Для ввода/вывода данных следует применять функции чтения/записи блоков памяти
Текстовый поток:
· Частный случай бинарного потока (файла);
· Рассматривается как последовательность символов;
· Некоторые служебные символы могут присутствовать в потоке, но не выводиться на экран;
· Для чтения/записи данных необходимо применять функции, аналогичные функциям консольного ввода / вывода (fprintf / fscanf, fputs / fgets, fputc / fgetc)
Двоичный ввод/вывод
При использовании текстового режима работы сфайлами при вводе/выводе данных выполняетсяпреобразование из строки или в строку.
Как результат:
· Потеря точности для вещественных чисел;
· Увеличение размера файла (каждая десятичная цифра в записи числа – отдельный ASCII-код, 8 битов);
· Замедление ввода/вывода
double num = 1./3.;
fprintf(fp, “%f”, num);
// вфайл - строка: “0.333333”
Функции чтения/записи данныхв двоичном виде работают сданными в том же виде, в какомони хранятся в программе.
Удобны для ввода/выводабольших объемов данных(массивов, структур и т.п.)известного размера.
Для работы с файлом, открытым в двоичном режиме,рекомендуется применятьтолько функции двоичноговвода/вывода, для работы сфайлом, открытом в текстовомрежиме – функции текстовоговвода/вывода.
Прототип функции | Назначение |
int fwrite (const char * array,size_t size,size_t count, FILE * stream); | Записывает блок данных в поток. Таким образомзапишется массив элементов array в текущую позицию впотоке. Для каждого элемента запишется size байт. Индикатор позиции в потоке изменится на число байт,записанных успешно. Возвращаемое значение будет равно count в случаеуспешного завершения записи. В случае ошибкивозвращаемое значение будет меньше count. |
int fread(void *ptr,size_t size,size_t count,FILE *stream); | Читает блок данных из потока. Будет прочитано countэлементов размером size каждый. Все они будут записаны впамять, начиная с адреса ptr. Индикатор позиции в потокеизменится на число байт, прочитанных успешно. Возвращаемое значение будет равно count в случаеуспешного завершения записи. В случае ошибкивозвращаемое значение будет меньше count. |
do { act_read = fread(buffer, sizeof(char), BUF_NUM, fin); fwrite(buffer, sizeof(char), BUF_NUM, fout); } while (act_read == BSIZE); |
Понятие указателя. Статические и динамические переменные. Функции для работы с динамической памятью. Примеры.
Ответ:
Виды указателей
Переменная-указатель предназначен для хранения адреса памяти
В языке Си различают три вида указателей:
· Указатель на объект.
· Указатель на void.
· Указатель на функцию.
Указатель не является самостоятельным типом, он всегда связан с каким-либо другим конкретным типов.
Понятие указателя
Переменная в программе – это объект, который имеет:
· Имя, которое соответствует адресу того участка памяти, который ей выделен.
· Тип, который соответствует размеру выделенного участка памяти.
· Значение, которое соответствует содержимому этого участка памяти.
· Имя и тип переменной (ее адрес и размер) не может изменяться, а значение – может изменяться.
Указатель – это переменная, которая содержит адрес другой переменной:
Независимо от того, на переменную какого типа (и размера) указывает указатель, размер переменной-указателя всегда постоянный и зависит от используемой модели памяти (как правило – 4 байта).
Причины использования указателей:
· Возможность просто передать адрес участка памяти (переменной) вместо копирования содержимого этого участка памяти (возможно очень большого размера)
· Возможность работы в разных местах программы с одним и тем же фрагментом данных, а не с его отдельными копиями
Объявление указателя
Синтаксис описания переменной-указателя:
тип * имя;
Где:
тип – тип переменной, на которую может указывать указатель;
имя – идентификатор переменной типа указатель, т.е. обычное имя переменной
Рекомендуется при объявлении переменной- указателя использовать префикс или суффикс ptr – это улучшит "читабельность" программы.
int *t;
double *ptr_d;
char *count_Ptr;
int **ptr_P;
К переменным-указателям на объекты можно применять следующие базовые операции (операторы):
· Присваивание (инициализация)
· Разыменование
· Взятие адреса переменной-указателя (адрес адреса)
· Сложение указателя с целочисленным значением
· Инкремент, декремент указателя
· Вычитание из указателя целочисленного значения
· Вычисление разности двух указателей
· Сравнение двух указателей (только для указателей на объекты одинакового типа)
Статические и динамические переменные
Адрес поименованной ячейки памяти также может определяться как на этапе компиляции, так и во время выполнения программы. По времени создания переменные бывают статическими и динамическими.
Первые создаются в момент запуска программы или подпрограммы, а вторые создаются в процессе выполнения программы.
Динамическая адресация нужна только тогда, когда количество поступающих на хранение данных заранее точно не известно. Такие данные размещают в специальных динамических структурах, тип которой выбирается в соответствии со спецификой задачи и с возможностями выбранной системы программирования. Это может быть стек, куча, очередь и т. п.
Для работы с динамической памятью в языке С используются следующие функции: malloc, calloc, free, realloc.
Рассмотрим их подробнее.
void *malloc(size_t size);
В качестве входного параметра функция принимает размер памяти, которую требуется выделить. Возвращаемым значением является указатель на выделенный в куче участок памяти.
Для выделения памяти под 1 000 000 int`ов необходимо выполнить следующий код:
int * p = malloc(1000000*sizeof(int));
Если ОС не смогла выделить память (например, памяти не хватило), то malloc возвращает 0.
После окончания работы с выделенной динамически памятью нужно освободить ее. Для этой цели используется функция free, которая возвращает память под управление ОС.
void free(void *ptr);
В качестве входного параметра в free нужно передать указатель, значение которого полученно из функции malloc. Вызов free на указателях полученных не из malloc (например, free(p+10)) приведет к неопределенному поведению. Это связанно с тем, что при выделении памяти при помощи malloc в ячейки перед той, на которую указывает возвращаемый функцией указатель операционная система записывает служебную информацию (см. рис.). При вызове free(p+10) информация находящаяся перед ячейкой (p+10) будет трактоваться как служебная.
void *calloc(size_t nmemb, size_t size);
Функция работает аналогично malloc, но отличается синтаксисом (вместо размера выделяемой памяти нужно задать количество элементов и размер одного элемента) и тем, что выделенная память будет обнулена. Например, после выполнения
int * q = (int *) calloc(1000000, sizeof(int))
q будет указывать на начало массива из миллиона int`ов инициализированных нулями.
void *realloc(void *ptr, size_t size);
Функция изменяет размер выделенной памяти (на которую указывает ptr, полученный из вызова malloc, calloc или realloc). Если размер указанный в параметре size больше, чем тот, который был выделен под указатель ptr, то проверяется, есть ли возможность выделить недостающие ячейки памяти подряд с уже выделенными. Если места недостаточно, то выделяется новый участок памяти размером size и данные по указателю ptr копируются в начало нового участка.