Указатели.
Указатель – это переменная, значением которой является адрес, по которому располагаются данные. Адрес – это номер ячейки памяти, в которой или с которой располагаются данные.
По типу данных в СИ указатели делятся на:
Типизированный указатель – указатель, содержащий адрес данных определенного типа (системного или пользовательского).
Не типизированный указатель – указатель, содержащий адрес данных неопределенного типа (просто адрес).
объявление указателя;
установка указателя;
обращение к значению, расположенному по указателю. Объявление (описание) указателя в языке СИ имеет следующий вид:
тип [near|far] *имя [=значение];
Указатель в СИ при объявлении можно инициализировать, указав через знак присвоения соответствующее значение. Данное значение должно быть адресом, записанном в одном из следующих виде:
•нулевое значение (идентификатор NULL);
•другой указатель;
•адрес переменной (через операцию взятия адреса);
•выражение, представляющее собой арифметику указателей;
•адрес, являющийся результатом выделения динамической памяти.
Пример:
#include <stdio.h>
int main()
{
int var; // обычная целочисленная переменная
int *ptrVar; // целочисленный указатель (ptrVar должен быть типа int, так как он будет ссылаться на переменную типа int)
ptrVar = &var; // присвоили указателю адрес ячейки в памяти, где лежит значение переменной var
scanf( "%d", &var ); // в переменную var положили значение, введенное с клавиатуры
printf( "%d\n", *ptrVar ); // вывод значения через указатель
getchar();
}
Результат выполнения: 6 6
Лекция №3.
Функции.
Функция – это синтаксически выделенный именованный программный модуль, выполняющий определенное действие или группу действий. Каждая функция имеет свой интерфейс и реализацию. Интерфейс функции – заголовок функции, в котором указывается название функции, список ее параметров и тип возвращаемого значения.
Описание функции на языке СИ осуществляется в любом месте программы вне описания других функций и состоит из трех элементов:
1. прототип функции;
2. заголовок функции;
3. тело функции.
Прототип функции – необязательная часть описания функции, предназначенная для объявления некоторой функции, интерфейс которой соответствует данному прототипу.Объявление прототипа имеет следующий вид:
тип имя(список типов формальных параметров);
Параметры функции – значения, передаваемые в функцию при ее вызове.
Заголовок функции – описание интерфейсной части функции, которая содержит: тип возвращаемого значения, имя функции и список формальных параметров функции. Синтаксис объявления заголовка функции:
тип имя(список формальных параметров)
Примеры заголовков функций:
int func(int i, double x, double y)
void func(int ind, char *string)
double func(void)
Тело функции – часть-реализация, содержащая программный код, выполняемый при вызове функции. Тело функции всегда следует сразу после заголовка функции (разделять их нельзя) и заключено в фигурные скобки.
Пример:
Реализация функции на СИ для вычисления факториала числа.
double factorial(unsigned);
...
double factorial(unsigned num)
{
double fact = 1.0;
for(unsigned i=1;i<=num;i++)
fact *= (double)i;
return fact;
}
Структуры.
Структура – это сложный тип данных представляющий собой упорядоченное в памяти множество элементов различного типа. Каждый элемент в структуре имеет свое имя и называется полем.
Объявление в СИ структуры имеет вид:
struct [имя типа]
{
поле_1;
поле_2;
...
поле_N;
} [список переменных];
Объявление полей структуры возможно только без инициализации. Если несколько полей следующих друг за другом в описании структуры имеют один и тот же тип, то для их описания можно использовать синтаксис объявления нескольких переменных одного и того же типа.
Файлы.
Файл – это именованная область данных на каком-либо носителе информации. Типы файлов (относительно языка «СИ»): текстовые; бинарные. Основные операции производимые над файлами: 1.Открытие файлов. 2.Чтение и запись данных. 3.Закрытие файлов.
Дополнительные операции: 1.Навигация по файлу. 2.Обработка ошибок работы с файлами. 3.Удаление и переименование файлов. 4.Описание переменной
Режимы открытия файлов с СИ
r |
только чтение |
w |
Только запись. Если файл существовал, то он переписывается. |
a |
Добавление: открытие файла для записи в конец, или создание файла. |
r+ |
Открывает файл для обновления (чтение и запись). |
w+ |
Открывает файл для обновления (чтение и запись), переписывая файл, если он существует. |
a+ |
Открывает файл для записи в конец файла или для чтения. |
Перенаправление потоков FILE * freopen(const char *filename, const char *mode, FILE *stream); Функция возвращает: Указатель на файл – все нормально, NULL – ошибка переопределения.
Закрытие файла int fclose(FILE *stream); stream - указатель на открытый файл. Функция возвращает: 0 – файл успешно закрыт. 1 – произошла ошибка закрытия файла. Проверка на достижение конца файла int feof(FILE *stream); stream - указатель на открытый файл. Функция возвращает: 0 – если конец файла еще не достигнут. !0 – достигнут конец файла.
Открытие текстовых файлов Во втором параметре дополнительно указывается символ t (необязательно): rt, wt, at, rt+, wt+, at+
Чтение из текстового файла
Форматированное чтение int fscanf(FILE *stream, const char * format, [arg] ...); Функция возвращает: >0 – число успешно прочитанных переменных, 0 – ни одна из переменных не была успешно прочитана, EOF – ошибка или достигнут конец файла. Чтение строки char * fgets(char * buffer, int maxlen, FILE *stream); Функция возвращает: buffer – все нормально, NULL – ошибка или достигнут конец файла. Чтение строки char * fgets(char * buffer, int maxlen, FILE *stream); Функция возвращает: buffer – все нормально, NULL – ошибка или достигнут конец файла. Чтение символа int fgetc(FILE *stream); Функция возвращает: код символа – если все нормально, EOF – если ошибка или достигнут конец файла. Помещение символа обратно в поток int ungetc(int c, FILE *stream); Функция возвращает: код символа – если все успешно, EOF – произошла ошибка.
Запись в текстовый файл в СИ
Форматированный вывод int fprintf(FILE *stream, const char *format, [arg] ...); Функция возвращает: число записанных символов – если все нормально, отрицательное значение – если ошибка. Запись строки int fputs(const char *string, FILE *stream); Функция возвращает: число записанных символов – все нормально, EOF – произошла ошибка. Запись символа int fputc(int c, FILE *stream); Функция возвращает: код записанного символа – все нормально, EOF – произошла ошибка. Открытие бинарных файлов Во втором параметре дополнительно указывается символ b (обязательно):rb, wb, ab, rb+, wb+, ab+ Чтение из бинарных файлов size_t fread(void *buffer, size_t size, size_t num,FILE *stream); Функция возвращает количество прочитанных блоков. Если оно меньше num, то произошла ошибка или достигнут конец файла. Запись в бинарный файл size_t fwrite(const void *buffer, size_t size, size_t num, FILE *stream); Функция возвращает количество записанных блоков. Если оно меньше num, то произошла ошибка.
Навигация по файлу
Чтение текущего смещения в файле: long int ftell(FILE *stream); Изменение текущего смещения в файле: int fseek(FILE *stream, long int offset, int origin); SEEK_SET (0) – от начала файла. SEEK_CUR (1) – от текущей позиции. SEEK_END (2) – от конца файла. Функция возвращает: 0 – все нормально, !0 – произошла ошибка. Перемещение к началу файла: void rewind(FILE *stream); Чтение текущей позиции в файле: int fgetpos(FILE *stream, fpos_t *pos); Установка текущей позиции в файле: int fsetpos(FILE *stream, const fpos_t *pos); Функции возвращают: 0 – все успешно, !0 – произошла ошибка. Структура fpos_t: typedef struct fpos_t { long off; mbstate_t wstate; } fpos_t; Получение признака ошибки: int ferror(FILE *stream); Функция возвращает ненулевое значение, если возникла ошибка. Функция сброса ошибки: void clearerr(FILE *stream); Функция вывода сообщения об ошибке: void perror(const char *string);
Буферизация
Функция очистки буфера: int fflush(FILE *stream); Функция возвращает: 0 – все нормально. EOF – произошла ошибка. Функция управления буфером: void setbuf(FILE *stream, char * buffer); Создает буфер размером BUFSIZ. Используется до ввода или вывода в поток.
Временные файлы
Функция создания временного файла: FILE * tmpfile(void); Создает временный файл в режиме wb+. После закрытия файла, последний автоматически удаляется. Функция генерации имени временного файла: char * tmpnam(char *buffer);
Удаление и переименование
Функция удаления файла: int remove(const char *filename); Функция переименования файла: int rename(const char *fname, const char *nname); Функции возвращают: 0 – в случае успеха, !0 – в противном случае.
Лекция №4.
Стек.
Стек (stack) является как бы противоположностью очереди, поскольку он работает по принципу "последним пришел — первым вышел" (last-in, first-out, LIFO). Чтобы наглядно представить себе стек, вспомните стопку тарелок. Первая тарелка, стоящая на столе, будет использована последней, а последняя тарелка, положенная наверх — первой. Стеки часто применяются в системном программном обеспечении, включая компиляторы и интерпретаторы.
При работе со стеками операции занесения и извлечения элемента являются основными. Данные операции традиционно называются "затолкать в стек" (push) и "вытолкнуть из стека" (pop). Поэтому для реализации стека необходимо написать две функции: push(), которая "заталкивает" значение в стек, и pop(), которая "выталкивает" значение из стека. Также необходимо выделить область памяти, которая будет использоваться в качестве стека. Для этой цели можно отвести массив или динамически выделить фрагмент памяти с помощью функций языка С, предусмотренных для динамического распределения памяти. Как и в случае очереди, функция извлечения получает из списка элемент и удаляет его, если он не хранится где-либо еше. Ниже приведена общая форма функций push() и pop(), работающих с целочисленным массивом. Стеки данных другого типа можно организовывать, изменив базовый тип данных массива.
int stack[MAX];
int tos=0; /* вершина стека */
/* Затолкать элемент в стек. */
void push(int i)
{
if(tos >= MAX) {
printf("Стак полон\n");
return;
}
stack[tos] = i;
tos++;
}
/* Получить верхний элемент стека. */
int pop(void)
{
tos--;
if(tos < 0) {
printf("Стек пуст\n");
return 0;
}
return stack[tos];
}
Переменная tos ("top of stack" — "вершина стека") содержит индекс вершины стека. При реализации данных функций необходимо учитывать случаи, когда стек заполнен или пуст. В нашем случае признаком пустого стека является равенство tos нулю, а признаком переполнения стека — такое увеличение tos, что его значение указывает куда-нибудь за пределы последней ячейки массива.
Пример работы со стеком.
|
|
Действие |
Содержимое стека |
push(A) |
A |
push(B) |
В А |
push(C) |
C B A |
рор() извлекает С |
В А |
push(F) |
F В А |
рор() извлекает F |
В А |
рор() извлекает В |
А |
рор() извлекает А |
пусто |
Стек будет размешаться в динамически распределяемой памяти, а не в массиве фиксированного размера. Хотя применение динамического распределения памяти и не требуется в таком простом примере, мы увидим, как использовать динамическую память для хранения данных стека.
Пример:
/* Простой калькулятор с четырмя действиями. */
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
int *p; /* указатель на область свободной памяти */
int *tos; /* указатель на вершину стека */
int *bos; /* указатель на дно стека */
void push(int i);
int pop(void);
int main(void)
{
int a, b;
char s[80];
p = (int *) malloc(MAX*sizeof(int)); /* получить память для стека */
if(!p) {
printf("Ошибка при выделении памяти\n");
exit(1);
}
tos = p;
bos = p + MAX-1;
printf("Калькулятор с четырьмя действиями\n");
printf("Нажмите 'q' для выхода\n");
do {
printf(": ");
gets(s);
switch(*s) {
case '+':
a = pop();
b = pop();
printf("%d\n", a+b);
push(a+b);
break;
case '-':
a = pop();
b = pop();
printf("%d\n", b-a);
push(b-a);
break;
case '*':
a = pop();
b = pop();
printf("%d\n", b*a);
push(b*a);
break;
case '/':
a = pop();
b = pop();
if(a==0) {
printf("Деление на 0.\n");
break;
}
printf("%d\n", b/a);
push(b/a);
break;
case '.': /* показать содержимое вершины стека */
a = pop();
push(a);
printf("Текущее значение на вершине стека: %d\n", a);
break;
default:
push(atoi(s));
}
} while(*s != 'q');
return 0;
}
/* Занесение элемента в стек. */
void push(int i)
{
if(p > bos) {
printf("Стек полон\n");
return;
}
*p = i;
p++;
}
/* Получение верхнего элемента из стека. */
int pop(void)
{
p--;
if(p < tos) {
printf("Стек пуст\n");
return 0;
}
return *p;
}
