Visual_Studio_2010
.pdfi = 1;
while ( !feof(fid)) { // пока не конец файла
// Вывод на консоль
printf("\n %3d\t\t\t %-17s %0.2f", number, name, sum);
// Запись в файл data77.dat
fprintf(fid2," %3d) %3d\t\t\t %-17s %0.2f\r\n",i++,number,name, sum);
// Чтение из файла data7.dat
fscanf_s(fid, "%d%s%Lf", &number, name, MAX, &sum);
}
fclose(fid);
fclose(fid2);
printf("\n\n\n Result see the files, \"%s\" and \"%s\"\n", file, file2);
printf("\n Press any key: ");
_getch(); return 0;
}
В программе использована функция feof(), которая проверяет, достигнут ли конец файла, связанного с потоком (указателем на файл) fid.
На рис.12.11 показан возможный результат выполнения программы.
Рис.12.11. Консольный вывод содержимого файла
Следует обратить внимание на прекращение ввода данных с клавиатуры с помощью комбинации клавиш Ctrl+Z.
Задание7
1. Вместо оператора цикла while примените оператор цикла for.
211
2.Отсортируйте записи владельцев ячеек по убыванию величины суммарной денежной суммы.
3.Подготовьте форматированный текстовый файл с именем compX, где Х – номер компьютера, на котором выполняется лабораторная работа. Затем информацию из файла выведите на консоль.
Пример 8. Написать программу пакетной записи в файл произвольного доступа массива данных и вывода этого пакета на консоль.
При решении примера применим функции fwrite() и fread() для бинарной записи и считывания информации.
Программный код решения примера
#include <stdio.h> #include <conio.h>
#define MAX 20
#define n 5 #define m 4
#define file "D:\\data8.txt"
int main(void)
{
//Матрица 5х4
int mass[MAX][MAX] = {
{1,2,3,4}, {5,6,7,8}, {9,10,11,12}, {13,14,15,16}, {17,18,19,20} };
int mass2[MAX][MAX]; // вспомогательная матрица int i, j;
// Массив из 5 указателей char *str[] = {
"Brian W. Kernighan",
"Dennis M. Ritchie", "Stephen Prata", "Herbert Shildt",
"The C Programming Language"
};
char *str2[n]; // вспомогательный массив указателей
FILE *fid;
if ( fopen_s(&fid, file, "wb") ) { fprintf(stdout, "\n\t File could not be opened\n"); printf("\n Error! Press any key: ");
_getch(); return -1; }
fwrite(str, sizeof(char), sizeof(str)/sizeof(char), fid); fwrite(mass, sizeof(int), sizeof(mass)/sizeof(int), fid);
fclose (fid);
212
if ( fopen_s(&fid, file, "rb") )
{
fprintf(stdout, "\n\t File could not be opened\n"); printf("\n Press any key: ");
_getch(); return -1;
}
fread(str2, sizeof(char), sizeof(str)/sizeof(char), fid); fread(mass2, sizeof(int), sizeof(mass2)/sizeof(int), fid);
// Чтение элементов из файла
printf("\n\t From a file \"%s\": \n\n", file); for (i = 0; i < n; ++i)
printf("\t %-15s\n", str2[i]);
printf("\n\t Matrix from a file \"%s\":\n", file); for (i = 0; i < n; ++i)
{
printf("\n\t");
for (j = 0; j < m; ++j) printf(" %3d", mass2[i][j]);
}
fclose(fid);
printf("\n\n\n Press any key: "); _getch();
return 0;
}
Функция fwrite()записывает в файл заданное количество байт из буфера памяти, адрес которого передан ей в качестве параметра. Это происходит с того места в файле, которое обозначено указателем позиции файла. Функция fread()считывает заданное количество байт, начиная с текущей позиции файла в буфер памяти, адрес которого передан ей в качестве параметра.
В программе пакетная запись информации – набора строк и матрицы целых чисел производится через двоичный поток с помощью функций fwrite(). Чтение информации из двоичного файла осуществляется функцией fread(). Форматы записи обеих функций одинаковы, так как в них требуется установить количество объектов с заданным размером байт, которые определяются функцией sizeof().
Результат выполнения программы представлен на рис.12.12 и 12.13.
213
Рис. 12.12. Результат чтения из файла бинарной информации
Рис.12.13. Результат записи в текстовый файл бинарной информации
Как видно из рис. 12.12 и 12.13, информация на консоли соответствует исходной информации, а в двоичном файле она не подлежит непосредственному восприятию.
П р и м е ч а н и е . Вид бинарной информации в текстовом файле зависит от установленных шрифтов.
Задание8
1.Запишите двоичную информацию в файлы с расширением .dat, .doc,
.bin. Проанализируйте файлы после их открытия.
2.Вместо массива указателей примените двухмерный символьный массив необходимой размерности.
3.Вместо массива указателей примените схему с двухуровневой адресацией.
4.Вместо двухмерного массива целых чисел примените целочисленный указатель.
5.Перезапишите информацию из двоичного файла в текстовый файл с именем compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа.
214
Контрольные вопросы
1.Что может быть файлом в языке С?
2.Какие обязательные операции выполняются при нормальной работе с файлами? Какие библиотечные функции при этом используются?
3.Как определяется текстовый поток в стандарте языка С?
4.Как определяется двоичный поток в стандарте языка С?
5.Что представляет собой указатель файла?
6.С помощью каких функций языка С осуществляются форматная запись в файл и форматное чтение данных из файла?
7.Какая переменная стандартной библиотеки используется для определения стандартного потока вывода на дисплей?
8.Какая переменная стандартной библиотеки используется для определения стандартного потока чтения с дисплея?
9.Как в языке С кодируется признак конца файла?
10.Как в языке С кодируется признак конца строки?
11.Что такое файл произвольного доступа?
12.Как в языке С осуществляется пакетная запись данных в файл?
13.Как осуществляется запись бинарной информации в текстовый файл?
14.Как осуществляется чтение бинарной информации из текстового файла?
БИБЛИОГРАФИЧЕСКИЙ СПИСОК
1.Демидович Е. М. Основы алгоритмизации и программирования. Язык Си : учеб. пособие/Е. М. Демидович. – 2-е изд., испр. и доп. – СПб. : БХВ-
Петербург, 2008. – 440 с.
2.Шилдт Г. Полный справочник по С : пер. с англ./Г. Шилдт. – 4-е изд. –
М. : Вильямс, 2007. – 704 с.
3.Керниган Б. У. Язык программирования С : пер. с англ./Б. У. Керниган, Д. М. Ритчи. – 2-е изд. – М. : Вильямс, 2007. – 304 с.
4.Прата С. Язык программирования С. Лекции и упражнения : пер. с англ. / С. Прата. – 5-е изд. – М. : Вильямс, 2006. – 960 с.
215
Тема 13
СТРУКТУРЫ – ПРОИЗВОДНЫЕ ТИПЫ ДАННЫХ ЯЗЫКА С
Рассматриваются вопросы создания и использования структур в языке программирования С.
ТЕОРЕТИЧЕСКАЯ ЧАСТЬ
Структура – это совокупность нескольких переменных, часто различных типов [1], объединяемых под одним именем. Переменные, из которых состоит структура, называются ее членами, элементами или полями [2]. С помощью структур удобно размещать в смежных полях связанные между собой элементы информации. Здесь могут быть собраны различные объекты – переменные, массивы, указатели, другие структуры и т. д. Если в массиве представлены переменные одного типа (например, float), то в структуре могут наличествовать переменные различных типов. Объявление структуры подготавливает шаблон, который можно использовать для создания ее объектов (экземпляров этой структуры) [2]. При объявлении структуры определяется агрегатный тип данных, но не переменная.
Определение структуры состоит из двух шагов:
1)объявление шаблона структуры (задание нового типа данных);
2)определение переменных типа объявленного шаблона [3].
Имена шаблонов должны быть уникальными в пределах области их определения, для того чтобы компилятор мог различать их типы. Задание шаблона осуществляется с помощью ключевого слова struct, за которым следуют его имя и список элементов, заключенных в фигурные скобки [3]. Имена элементов в одном шаблоне также должны быть уникальными. Задание только шаблона не влечет резервирования памяти компилятором. Шаблон предоставляет компилятору необходимую информацию об элементах структурной переменной для выделения места в оперативной памяти и организации доступа к ней при определении структурной переменной и использовании ее отдельных элементов [3].
Рассмотрим шаблон структуры для определения имени и фамилии работника (служащего – employee), его возраста, почасовой оплаты:
struct employee { |
|
char Name [21]; |
// имя |
char Surname [21]; |
// фамилия |
int age; |
// возраст |
double hourlysalary; |
// почасовой оклад |
}; |
|
Как видно, шаблон структуры struct employee состоит из символьных массивов типа char, целого числа типа int и числа с плавающей точкой двойной точности типа double. Описание элементов производится в фигурных скобках, после закрывающей скобки обязательна точка с запятой.
216
Определение структуры начинается с ключевого слова struct. Иденти-
фикатор employee является именем-этикеткой (tag name), дескриптором,
именует структуру и используется совместно с ключевым словом struct для объявления переменных структурного типа [4].
Переменные структуры объявляются так же, как и переменные других типов. На основе приведенного шаблона сделаем несколько объявлений новых структурных переменных:
struct employee new_ employee, * employeePtr, stack[120];
Здесь new_employee – переменная типа struct employee, *employeePtr – указатель на struct employee, stack[120] – массив из 120 элементов типа struct employee.
Переменные new_employee, *employeePtr, stack[102] могут быть объединены с определением структуры, а именно:
struct employee { |
|
|
char Name [20+1]; |
// имя |
|
char Surname [20+1]; |
// фамилия |
|
int age; |
// |
возраст |
double hourlysalary; |
// |
почасовой оклад |
}new_employee, *employeePtr, stack[120];
Имя-этикетка не является для структуры обязательным. Если определение структуры не содержит его, то переменные для этой структуры могут быть объявлены только в ее определении, но не отдельно [1].
При создании структур часто используется ключевое слово typedef (оператор). Оно предоставляет программисту средство создания синонимов (или псевдонимов) для ранее определенных типов данных. К нему часто обращаются для того, чтобы дать укороченное имя структурному типу [4]. Например, оператор вида
typedef struct card Card;
определяет новый тип с именем Card как синоним типа struct card. Ключевое слово typedef может быть использовано в определении типа
структуры без имени-этикетки. Например,
typedef struct |
{ |
char *Name; |
// имя |
char *Surname; |
// фамилия |
int age; |
// возраст |
double hourlysalary; // почасовой оклад
}Man;
создает тип Man без использования отдельного оператора typedef. В данном примере необходимо предусмотреть выделение памяти под символьные указатели.
Созданный тип Man можно использовать для объявления структурных переменных типа struct employee, например
Man stack[120];
П р и м е ч а н и е . Использование typedef помогает сделать программу более переносимой.
217
Синтаксис инициализации структур аналогичен инициализации массивов. Например, выполним инициализацию структуры:
struct employee { |
|
|
char Name [20+1]; |
// имя |
|
char Surname [20+1]; |
// фамилия |
|
int age; |
// возраст |
|
double hourlysalary; |
// почасовой оклад |
|
} |
new_employee; |
|
Возможный вариант инициализации:
struct employee { |
|
char Name [20]; |
// имя |
char Surname [20]; |
// фамилия |
int age; |
// возраст |
double hourlysalary; |
// почасовой оклад |
}new_employee = { "Peter", "Smith", 25, 6.78 };
При инициализации структуры ее элементы (инициализаторы) должны соответствовать заданному типу и отделяться друг от друга запятыми.
Доступ к элементам (компонентам, полям) структуры осуществляется двумя способами:
1)с помощью оператора связывающей точки (оператора точки) – . при непосредственной работе со структурой;
2)с помощью стрелки –> при использовании указателей на структуры.
Общий формат доступа к элементам структуры имеет вид
имя_переменной_структуры . имя_поля;
имя_указателя_на_структуру –> имя_поля; (*имя_указателя_на_структуру) . имя_поля;
Например, для рассмотренной инициализации можно изменить почасовой оклад с помощью оператора точки и указателя на структуру:
new_employee.hourlysalary = 21.0; employeePtr –> hourlysalary = 21.0;
Следует обратить внимание на то, что new_employee – это имя всего объекта-структуры, а hourlysalary – имя элемента этой структуры. Аналогично и в случае использования указателя *employeePtr на структуру.
При изменении полей структуры следует обратить внимание на случай со строками, например:
strcpy(new_employee . name, "Stephen");
При этом должен быть предусмотрен заголовочный файл <string.h>. для функции strcpy().
Когда объявлен массив структур, например stack[120], это означает, что создано 120 наборов переменных, каждый из которых организован так, как определено в структуре с дескриптором employee. Чтобы получить доступ к определенной структуре stack[120], следует указать имя массива с индексом, который изменяется от 0 до 119. Например, для пятой структуры можно сделать изменения в почасовом окладе:
218
stack[4].hourlysalary = 121.5;
Как и в других массивах переменных языка С, в массивах структур индексирование начинается с нуля. Членами структуры могут быть также массивы или структуры. Когда одна структура является членом другой, она называ-
ется вложенной [2].
Рассмотрим пример:
struct X = { int A[7][8]; float b; char ch;
struct employee Emp2; } Y;
Инициализация элементов двухмерного массива А может быть такой:
Y.A[2][5] = 99;
Инициализация вложенной структуры может быть следующей:
Y.Emp2.hourlysalary = 12.75;
В соответствии со стандартом С89 структуры могут быть вложенными вплоть до 15-го уровня, стандарт С99 допускает уровень вложенности до 63-го включительно [2].
ПРАКТИЧЕСКАЯ ЧАСТЬ
Пример 1. Написать программу структурного описания каталога одной книги. Программный код решения примера
#include <stdio.h> |
|
|
#include <conio.h> |
|
|
#include <string.h> |
|
|
#define |
N 40 |
|
struct |
book { // определение структуры |
|
char title[N+1]; |
// название книги |
|
char author[N+1]; |
// автор |
|
int year; |
// год издания |
|
|
|
|
int page; |
// количество страниц |
|
float price; |
// цена в у.е. |
|
} Library; |
|
|
|
|
|
int main (void)
{
// Инициализация полей структуры
Library.year = 2007;
Library.page = 496;
Library.price = 12.78F;
strcpy_s(Library.title, N, "Programming in C"); strcpy_s(Library.author, N, "Stephen G. Kochan");
219
// Вывод на консоль
printf("\n\t Title: %s\n", Library.title); printf("\t Author: %s\n", Library.author); printf("\t Year: %d\n", Library.year );
printf("\t Number of pages: %d p.\n", Library.page ); printf("\t Price: %1.2f y.e.\n", Library.price);
printf("\n\n Press any key: "); _getch();
return 0;
}
В программе использованы функции strcpy_s() вместо стандартных функций strcpy(), что позволило избавиться от предупреждений в системе
Visual Studio 2010.
Результат выполнения программы показан на рис.13.1.
Рис. 13.1. Пример инициализированных полей структуры
Задание1
1.Совместите объявление и инициализацию структуры. После инициализации структуры выполните изменение ее полей с последующим выводом на консоль.
2.Примените оператор typedef.
3.Поля структуры title и author определите с помощью указателей.
4.Произведите инициализацию структуры после ввода значений полей с клавиатуры.
5.Выполните вывод полей структуры в текстовый файл compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа.
Пример 2. Написать программу анализа средней успеваемости четырех студентов по четырем предметам за сессию на основе структурного типа данных.
Программный код решения примера
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#define N 4 // число студентов
#define CH 30 // число символов для фамилии или имени
220