Добавил:
okley
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Программирование C++ (1 сем, мага) / Лабы / Лабораторная работа №5
.pdf
Министерство науки и высшего образования Российской Федерации
Федеральное государственное автономное образовательное учреждение высшего
образования
«Национальный исследовательский ядерный университет «МИФИ»
Кафедра экспериментальной ядерной физики и космофизики (№7)
Программирование на С и С++
Отчет по лабораторной работе № 5
Выполнил:
студент группы M22-105 Васильева А.П.
Москва, 2022

Цель работы: знакомство со структурами и работой с файлами.
Задача: дан файл, содержащий набор чисел. Необходимо создать структуру, которая
могла бы хранить эти числа. Выполнить чтение чисел из файла с последующим сохранением
их в структурные переменные. Реализовать операцию сложения этих чисел.
Введение
Структуры — один из примеров составных типов данных, называемых агрегатными
типами.
Структурные переменные (структуры) – это объединение одной или более
переменных, возможно, разных типов, в одну область памяти, имеющую одно имя.
Отдельные составные части структурной переменной называются полями.
Как и любая переменная, структурная переменная должна описываться. Ее описание
состоит из двух этапов:
1) задание шаблона структуры;
2) собственно описание структурной переменной.
Каждый шаблон имеет собственное имя для того, чтобы компилятор мог различать
различные шаблоны. В том случае, если в функции используется единственный шаблон, он
может не иметь имя, что задается пропуском имени при описании шаблона. Имена шаблонов
должны быть уникальными в пределах их области определения, и, в частности, в пределах
одной функции может быть только один безымянный шаблон.
Синтаксис задания шаблона таков:
struct имя_шаблона {
тип_1 имя_поля_1;
тип_2 имя_поля_2;
•••
тип_N имя_поля_N;
};
Здесь имя_шаблона – имя шаблона, удовлетворяющее правилам задания
идентификаторов языка С++ (С); тип_1, тип_2, ..., тип_N – любые типы, например
int, char, float; имя_поля_1, имя_поля_2, ..., имя_поля_N – имена полей,
удовлетворяющие правилам задания идентификаторов языка С++ (С).

Когда задан шаблон, может быть описана структурная переменная. Описание
структурной переменной состоит из задания типа и имени структурной переменной.
Файл - набор взаимосвязанных данных, имеющих общее имя, находящихся на каком-
либо устройстве и доступных для обработки на компьютере. Существует два подхода по
работе с файлами:
• информация о потоке (файле) заносится в структуру типа FILE, определенную в файле
stdio.h, и файл оказывается связанным с этой структурой;
• файл связывается с дескриптором – целым значением, характеризующим размещение
информации о файле во внутренних таблицах системы.
В начале работы любой программы автоматически открывается три потока со своими
дескрипторами:
Поток
Дескриптор
stdin
0
Стандартный входной поток – обычно клавиатура
stdout
1
Стандартный выходной поток – обычно экран
stderr
2
Стандартный поток сообщений об ошибках

Ход работы
Файл, содержащий набор чисел, имел следующий вид:
Файл содержит как действительные, так и комплексные числа разных знаков. Причем
целая часть чисел содержит 5 разрядов, а дробная 15. Для хранения этих чисел создана
структура longNumber:

Структура содержит следующие поля:
• int whole[7] – массив, содержащий 7 элементов типа int. Этот массив
предназначен для хранения вещественной целой части числа. Причем элемент массива
с индексом 0 – whole[0] хранит информацию о знаке вещественной части: 0,
если “+”, и 1, если “—”. Разряду единиц соответствует элемент массива whole[6],
разряду десятков – whole[5] и т.д.
• int fractional[15] – массив, содержащий 15 элементов типа int. Предназначен
для хранения вещественной дробной части числа. Разряду десятой доли числа
соответствует fractional[0], разряду сотой доли – fractional[1] и т.д.
• int wholeComplex[7] – массив, содержащий 7 элементов типа int. Этот массив
предназначен для хранения мнимой целой части числа. Причем элемент массива с
индексом 0 – wholeComplex[0] хранит информацию о знаке мнимой части: 0, если
“+”, и 1, если “—”. Разряду единиц соответствует элемент
массива wholeComplex[6], разряду десятков – wholeComplex[5] и т.д.
• int fractionalComplex[15] – массив, содержащий 15 элементов типа int.
Предназначен для хранения мнимой дробной части числа. Разряду десятой доли числа
соответствует fractionalComplex [0], разряду сотой доли –
fractionalComplex [1] и т.д.
• bool isComplex – значение этого поля равно true, если число содержит мнимую
часть, и false в противном случае.
Несмотря на то, что целая часть чисел, содержащихся в файле, содержит только
5 разрядов, массивы для её хранения состоят из 7 элементов – нулевой элемент, как уже
было описано выше, содержит информацию о знаке вещественной или мнимой части числа,
а следующий за ним элемент при считывании чисел из файла заполняется нулями, но он
необходим при выполнении операции сложения чисел, т.к. там может происходить переход
в следующий разряд.
Запись значения каждого из разрядов числа в структурную переменную осуществлена
с помощью функции writeNumber(…). Функция принимает в качестве аргументов
указатель на структурную переменную (longNumber *number); цифру, содержащуюся в
разряде числа (int symbol); часть числа, в которую происходит запись
(int currentPart) и индекс элемента массива, куда должна быть записана данная
цифра.

Если currentPart = 0, то запись происходит в вещественную целую часть, –
элементу массива whole[j] структуры number присваивается значение symbol.
Если currentPart = 1, то запись происходит в вещественную дробную часть, –
элементу массива fractional[j] структуры number присваивается значение symbol.
Если currentPart = 2, то запись происходит в мнимую целую часть, – элементу
массива wholeComplex[j] структуры number присваивается значение symbol.
Иначе запись происходит в мнимую дробную часть, – элементу массива
fractionalComplex[j] структуры number присваивается значение symbol.
Вызов функции осуществляется из блока main() программы, в котором и происходит
считывание символов из файла.
Информация о файле заносится в структуру inFile типа файл. Файл открывается
функцией fopen(“task1a.txt”, “r”), где “task1a.txt” – имя файла, “r” –
аргумент, определяющий режим открытия файла – открыть файл только для чтения.
Создается массив структур struct longNumber numbersArray[13],
содержащий 13 элементов, т.к. в файле 13 чисел. Создаются перемнные типа int – для
индексации элементов массива структур (i), для индексации элементов массивов внутри
структуры (j) и для хранения информации о том, в какую часть числа идет
запись (currentPart). Также создаются переменные типа char для хранения текущего
считанного символа (symbol) и предыдущего символа (prevSymbol).
Далее проверяется, что файл открыт if (inFile != NULL) {…}, и если это так, то
начинается чтение файла в цикле while. Считывается символ из файла с помощью
функции fscanf и сохраняется в переменную symbol типа char. Считывание будет
происходить до тех пор, пока не будет достигнут конец файла:
while (fscanf(inFile, "%c", &symbol) != EOF) { … }
Далее выполняется ряд проверок, стобы определить, является ли считанный символ
цифрой одного из чисел и если да, то в какую часть числа его необходимо записывать.


Первый блок if:
if (currentPart == 0 and j == 2 and std::isdigit(symbol)) {…}
соответствует случаю, когда начинается новое число, т.к. переменным currentPart и j
после окончания записи каждого числа будут присваиваться значения 0 и 2 соответственно.
Изначально эти переменные также были инициализированы этими значениями. В этом
блоке проверяется, чему был равен предыдущий символ и, если он равен “-”, то
numbersArray[i].whole[0] присваивается значение 1 (число отрицательное),
иначе – 0 (число положительное). А элементу numbersArray[i].whole[1]
присваивается значение 0, т.к. числа в файле не содержат разряда сотен тысяч. Запись
символов в структурную переменную осуществляется с помощью описанной выше
функции writeNumber(…).
Блок if (std::isdigit(prevSymbol) and !std::isdigit(symbol)){…}
соответствует случаю, когда закончилось считывание какой-либо из частей числа
(предыдущий символ – цифра, а текущий цифрой не является). Он внутри себя содержит
ещё ряд проверок:
1. if (symbol == '.') {…} – т.к. точка является разделителем целой и дробной
частей числа, этот блок соответствует ситуации, когда закончилось считывание
целой части и произошел переход к считыванию дробной. Здесь переменная
currentPart увеличивается на 1 (для целых вещественной и мнимой частей
она равна 0 и 2 соответственно, а для дробных – 1 и 3), а переменной j
присваивается значение 0, т.к. десятым долям соответствуют элементы массивов
fractional и fractionalComplex с индексом ноль.
2. else if (symbol == '+' or symbol == '-') {…} – закончилось
считывание вещественной дробной записи числа и далее следует комплексная
часть. Здесь переменной currentPart присваивается значение 2,
переменной j – 2, numbersArray[i].isComplex присваивается значение
true.
3. else if (currentPart == 1) {…} – закончилось считывание
вещественной дробной части числа, но мнимой части не последовало, т.е. число
действительное и полностью считано. В этом блоке
numbersArray[i].isComplex присваивается значение false, а также
заполняются нулями массивы numbersArray[i].wholeComplex и
numbersArray[i].fractionalComplex. Переменным currentPart и j
присваиваются значения 0 и 2 соответственно, т.к. текущее число закончено и,
как только снова в потоке символов встретится цифра, начнется запись
следующего. По этой же причине i увеличивается на единицу.
4. else {…} – также соответствует случаю, когда закончилось считывание числа,
однако этот блок выполняется для окончания записи комплексных числа.
Переменным currentPart и j присваиваются значения 0 и 2 соответственно,
т.к. текущее число закончено и, как только снова в потоке символов встретится
цифра, начнется запись следующего. По этой же причине i увеличивается на
единицу.

Блок if (std::isdigit(symbol)) {…} выполняется, если текущий считанный
символ – цифра. В этом случае с помощью функции writeNumber(…) в структурную
переменную numbersArray[i] запишется symbol – элементу массива с индексом j,
соответствующему части числа currentPart, присвоится значение
static_cast<int>(symbol)-‘0’. Здесь выполняется преобразование типа char к
типу int. Т.к. результатом static_cast<int>(symbol) будет код символа symbol,
а не число ему соответствующее, для получения числа необходимо вычесть ‘0’ – код
символа “0”, с которого начинаются символы.
Перед завершением каждой итерации цикла while переменной prevSymbol
присваивается значение symbol, чтобы в следующей итерации было доступно значение как
текущего, так и предыдущего символов.
Таким образом осуществляется чтение файла и запись чисел в массив структур. После
завершения считывания файл закрывается с помощью функции fclose().
После заполнения массива структур его элементы имеют следующий вид:
Рассмотрим теперь, как реализовано сложение чисел.
Для того, чтобы сложить два числа, хранящихся в структурных переменных, написана
функция stuct longNumber sumNumbers(longNumber *a, longNumber *b):
Функция принимает в качестве аргументов два указателя – каждый из которых
указывает на структуру longNumber и возвращает объект типа longNumber.

Внутри функции создается структурная переменная struct longNumber result,
которая будет содержать результат сложения. Далее дважды вызывается функция
sumRealOrImagParts(…), в которую первый раз передаются указатели на первые
элементы массивов вещественных целых и дробных частей складываемых чисел, а так же
массивов, в которые будет записан результат сложения. Во второй раз передаются мнимые
части. После этого, если одна из складываемых переменных была комплексной, полю
isComplex результата сложения присваивается true, в противном случае присваивается
false.
Функция void sumRealOrImagParts(int *wholeA, int *fractionalA,
int *wholeB, int *fractionalB, int *wholeResult, int
*fractionalResult) может выполнять по отдельности сложение как комплексной, так
и вещественной частей числа.
В качестве аргументов функция получает:
• int *wholeA – указатель на первый элемент массива, содержащего целую часть
первого из складываемых чисел;
• int *fractionalA – указатель на первый элемент массива, содержащего дробную
часть первого из складываемых чисел;
Соседние файлы в папке Лабы
