У
тверждаю
Ректор университета
_______________ О.Н. Федонин
« ___ » _____________2017 г.
Методические указания
к выполнению лабораторной работы
У
ДК
004.432.2
Алгоритмические языки. Массивы и файлы [Электронный ресурс]: методические указания к выполнению лабораторной работы. – Брянск: БГТУ, 2017. –13с.
Разработали:
А.П. Горлов, канд.техн.наук., доц.
М.Л. Гулак, канд.техн.наук., доц.
Рекомендовано кафедрой «Системы информационной безопасности» БГТУ (протокол № 11 от 28.08.2017).
Научный редактор М.Ю. Рытов
Компьютерный набор А.П. Горлов
Подписано в печать 28.08.17 г. Формат 60х84 1/16. Усл. печ.л. 0,75 Уч.-изд.л. 0,75 |
Брянский государственный технический университет
Кафедра «Системы информационной безопасности», 58-83-54
241035, Брянск, бульвар 50 лет Октября, 7.
Цель работы – получение навыков работы с массивами при решении задач, а также реализация ввода и вывода данных при помощи файлов.
Продолжительность работы: 4 ч.
При решении задач с большим количеством данных одинакового типа использование переменных с различными именами, не упорядоченных по адресам памяти, затрудняет программирование. В подобных случаях в языке Си используют объекты, называемые массивами.
Массив - это непрерывный участок памяти, содержащий последовательность объектов одинакового типа, обозначаемый одним именем.
Элемент массива (значение элемента массива) – значение, хранящееся в определенной ячейке памяти, расположенной в пределах массива, а также адрес этой ячейки памяти.
Каждый элемент массива характеризуется тремя величинами:
адресом элемента - адресом начальной ячейки памяти, в которой расположен этот элемент;
индексом элемента (порядковым номером элемента в массиве);
значением элемента.
Адрес массива – адрес начального элемента массива.
Имя массива – идентификатор, используемый для обращения к элементам массива.
Размер массива – количество элементов массива
Размер элемента – количество байт, занимаемых одним элементом массива.
Для объявления массива в языке Си используется следующий синтаксис:
тип имя[размерность]={инициализация};
Инициализация представляет собой набор начальных значений элементов массива, разделенных запятыми.
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // массив a из 10 целых чисел
int a[10] = {0}; // массив a из 10 элементов, инициализированных 0
Если массив проинициализирован при объявлении, то константные начальные значения его элементов указываются через запятую в фигурных скобках. В этом случае количество элементов в квадратных скобках может быть опущено.
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
При обращении к элементам массива индекс требуемого элемента указывается в квадратных скобках[].
Однако часто требуется задавать значения элементов массива в процессе выполнения программы. При этом используется объявление массива без инициализации. В таком случае указание количества элементов в квадратных скобках обязательно.
int a[10];
Для задания начальных значений элементов массива очень часто используется параметрический цикл:
// Ввод элементов массива
for(i=0; i<5; i++) {
printf("a[%d] = ", i);
scanf("%d", &a[i]); // &a[i] - адрес i-го элемента массива
}
// Вывод элементов массива
for(i=0;i<5;i++) {
printf("%d ",a[i]); // пробел в формате печати обязателен
Обработку массивов удобно организовывать с помощью специальных функций. Для обработки массива в качестве аргументов функции необходимо передать
адрес массива,
размер массива.
Исключение составляют функции обработки строк, в которые достаточно передать только адрес.
При передаче переменные в качестве аргументов функции данные передаются как копии. Это означает, что если внутри функции произойдет изменение значения параметра, то это никак не повлияет на его значение внутри вызывающей функции.
Если в функцию передается адрес переменной (или адрес массива), то все операции, выполняемые в функции с данными, находящимися в пределах видимости указанного адреса, производятся над оригиналом данных, поэтому исходный массив (или значение переменной) может быть изменено вызываемой функцией.
Пример . Функция вывода элементов массива.
void f(int *x, int n) {
// x - указатель на массив (адрес массива)
// n - размер массива
for(int i=0; i<n; i++)
printf(“%d”,x[i]);
}
Заполнить массив случайными числами в Си довольно просто. В этом случае нам потребуются функции rand(); и srand (m); из библиотеки stdlib.h.
Пусть мы имеем одномерный массив из десяти целочисленных элементов, тогда заполнение такого массива случайными числами будет выглядеть вот так:
for ( int i = 0; i < 10; i ++)
Arr [i] = rand ()%100;
Этот цикл заполняет массив Arr случайными числами от 0 до 100. При этом число 100 не включается в диапазон.
Использование функции srand (time (NULL)); позволяет получить при каждом запуске программы различные числа, поскольку устанавливает для функции rand(); в качестве начального значения текущее время. В противном случае мы получим одинаковые значения элементов массива при каждом запуске программы.
Для использования функций в заголовок требуется подключить следующие библиотеки:
#include <stdlib.h>
#include <ctime>
Пример:
// функция генерации случайного числа из диапазона от range_min до range_max включительно
int rrand(int range_min, int range_max) {
return rand() % (range_max - range_min + 1) + range_min;
}
В стандартной библиотеке языка Си предусмотрены функции для работы с файлами. Для работы с ними необходимо подключить заголовочный файл <stdio.h>.
Работа с файлами складывается из трех шагов:
• открыть файл;
• работать с файлом;
• закрыть файл.
Рассмотрим эти шаги подробнее.
1. Файл открывается. Это означает, что программа "захватывает" заданный по имени файл, сообщает операционной системе, что далее она будет с ним работать. Данный шаг нужен, чтобы не возникало конфликтов, когда несколько программ одновременно хотят записывать информацию в один и тот же файл. Правда, считывать данные из файла, очевидно, допустимо одновременно множеством программ, поэтому в операции открытия файла обычно уточняется, что файл открывается "на чтение" (считывание информации, которая не меняется) либо "на запись" (данные в файле модифицируются).
Функция открытия файла имеет следующий прототип (здесь и далее пропущены некоторые платформенные особенности прототипов):
FILE * fopen (const char *filename, const char *opentype)
Эта функция передаёт запрос на открытие файла операционной системе. Операционная система при открытии файла для некоторой программы сообщает ей (программе) идентификатор (как правило, целое число), которое идентифицирует в программе в дальнейшем нужный открытый файл. Этот идентификатор запоминается в переменной; обычно такая переменная называется файловой переменной. Файловая переменная в стандартной библиотеке языка Си представляет собой структуру типа FILE и помимо идентификатора файла хранит также дополнительные данные о текущем состоянии работы с файлом. Её часто называют дескриптором файла. Функция fopen возвращает указатель на динамически созданную структуру типа FILE.
Параметров у функции fopen() два. Первый – это путь к файлу (строка), второй - параметры открытия файла. Используются следующие способы открытия файла:
режим |
описание |
начинает с … |
||
r |
rb |
|
открывает для чтения |
начала |
w |
wb |
|
открывает для записи (создаёт файл в случае его отсутствия). Удаляет содержимое и перезаписывает файл. |
начала |
a |
ab |
|
открывает для добавления (создаёт файл в случае его отсутствия) |
конца |
r+ |
rb+ |
r+b |
открывает для чтения и записи |
начала |
w+ |
wb+ |
w+b |
открывает для чтения и записи. Удаляет содержимое и перезаписывает файл. |
начала |
a+ |
ab+ |
a+b |
открывает для чтения и записи (добавляет в случае существования файла) |
конца |
2. Ведется работа с файлом. Из него данные либо считываются, либо в него записываются. Приёмы работы с файлом зависят от типа контента файла и будут рассмотрены далее.
3. Файл закрывается. После этой операции он снова доступен другим программам для обработки.
Функция закрытия файла имеет следующий прототип:
int fclose (FILE *stream);
При вызове функции fclose нужно передать указатель на дескриптор ранее открытого файла. Вызов функции fclose приводит к закрытию потока данных, на который указывает аргумент stream, сбросу остаточного содержимого буфера данных в файл (если в буфере оставались данные), освобождению памяти выделенной под буфера чтения и записи (если буфера создавались) и закрытию файла связанного с потоком данных. В случае успешной работы функция возвращает 0. Если во время работы функции возникла ошибка, то она возвращает EOF (-1), при этом в дескриптор файла будет присвоен код ошибки, которую затем можно определить, используя функцию ferror.
Вот список некоторых дополнительных полезных функций для работы с файлами:
int feof (FILE *stream); //проверка достижения конца файла
int fseek(FILE *stream, long offset, int origin); //позиционирование
int ferror(FILE *stream); // возвращает текущий код ошибки
void clearerr (FILE* stream); // сбрасывает признак ошибки в 0
// записывает в pposition текущую позицию курсора в файле
int fgetpos(FILE* stream, fpos_t* pposition);
// устанавливает курсор в файле в позицию из pposition
int fsetpos (FILE*stream, const fpos_t* pposition);
Для работы с текстовыми файлами предназначены функции, аналогичные функциям для работы со стандартным устройством ввода/вывода, и отличаются от них наличием дополнительного аргумента: указателя на дескриптор файла. Вот список основных функций для работы с текстовыми файлами:
int fprintf (FILE *stream, const char *template, ...);
int fscanf (FILE *stream, const char *template, ...);
int fgetc (FILE*);
char*fgets (char*, int, FILE*);
int fputc (int, FILE*);
int fputs (const char*, FILE*);