Электронный конспект по программированию часть 1. Программа на языке Си, так же как и на большинстве современных языков программирования, создается в два этапа
Скачать 32.35 Mb.
|
strcpy ( b2.author, b1.author ); strcpy ( b2.title, b1.title ); b2.year = b1.year; b2.pages = b1.pages; Можно использовать специальную функцию memcpy, которая умеет копировать блоки памяти.Для ее использования надо подключить к программе заголовочный файл mem.h. #include Book b1, b2; // здесь нужно заполнить структуру b1 memcpy(&b2, &b1, sizeof(Book)); // куда, откуда, сколько байт Самый простой способ – третий. Достаточно просто написать b2 = b1; При этом программа скопирует одну структуру в другую «бит в бит». Зачем же рассказыватьпро остальные два способа? Только для того, чтобы понять, как это все работает, посколькунепонимание ведет к трудноуловимым ошибкам. Массивы структур Структуры служат для обработки большого объема информации, поэтому чаще всего в программе используются массивы структур. Они объявляются так же, как обычно, но предварительно (выше) надо объявить саму структуру как новый тип данных. Для обращения к полю структуры также используют точку, но теперь надо указать в квад- ратных скобках еще номер нужной структуры, например Book A[20]; ... A[12].pages = 50; for ( i = 0; i < 20; i ++ ) // цикл по всем структурам в массива puts(A[i].title); // вывести название книги Если вы работаете с двоичными файлами, то чтение и запись всего массива структур выполняется в одну строчку. Покажем приемы работы с двоичным файлом на примере. Пример. В файле books.dat записаны структуры типа Book. Известно, что их не больше 100. Требуется прочитать их в память, у всех книг установить 2009 год издания и записать обратнов тот же файл. Поскольку по условию известно, что структур не больше 100, заранее выделяем в памяти массив на 100 структур. Book b[100]; При чтении из файла пытаемся читать все 100 структур: n = fread ( &b[0], sizeof(Book), 100, fp ); Чтобы определить, сколько структур было в действительности прочитано, используем значениеn, которое функция fread возвращает в качестве результата. Вот полная программа: #include struct Book { // объявление нового типа данных char author[40]; char title[80]; int year; int pages; }; main() { Book b[100]; // выделение памяти под массив структур int i, n; FILE *fp; fp = fopen("books.dat", "rb"); // читаем 100 структур n = fread(&b[0], sizeof(Book), 100, fp); // прочитали n шт. fclose ( fp ); for ( i = 0; i < n; i ++ ) // обрабатываем все, что прочитали b[i].year = 2009; fp = fopen("books.dat", "wb"); // записываем n шт. fwrite ( b, sizeof(Book), n, fp ); fclose ( fp ); } Динамическое выделение памяти Предположим, что надо создать массив структур, размер которого выясняется только во время работы программы. Для этого надо 1) объявить переменную типа указатель на нужный тип данных; 2) выделить память с помощью оператора new и запомнить адрес выделенного блока; 3) использовать новую область как обычный массив. Book *B; int n; printf("Сколько у вас книг? "); scanf ( "%d", &n ); // вводим размер массива B = new Book[n]; // выделяем память // здесь работаем с массивом B, как обычно delete B; // освобождаем память Иногда требуется выделить в памяти одну структуру. При этом мы получаем ее адрес, который записываем в переменную-указатель. Как получить доступ к полю структуры? Один из вариантов – «разыменовать» указатель, то есть обратиться к той структуре, на которую он указывает. Если p – указатель на структуру типа Book, то обратиться к ее полюauthor можно как (*p).author. Эта запись означает «мне нужно поле author той структуры, на которую указывает указатель p». В языке Си существует и другой способ обратиться к полю структуры: можно написать и p->author, что значит то же самое, что и (*p).author, но более понятно. В следующем примере динамически выделяется память на 1 структуру, ее поля считываются с клавиатуры, изатем структура записывается в конец текстового файла books.txt. Book *p; FILE *fp; p = new Book; // выделить память на 1 структуру printf("Автор "); // ввод полей через указатель gets ( p->author ); printf("Названиекниги "); gets ( p->title ); printf("Год издания, кол-во страниц "); scanf("%d%d", &p->year, &p->pages); fp = fopen("books.txt", "a"); // дописать в конец файла fprintf("%s\n%s\n%d %d\n", // обращение через указатель p->author, p->title, p->year, p->pages); fclose ( fp ); delete p; // освободить память Структуры как параметры процедур Структуры, так же, как и любые другие типы, могут быть параметрами функций и процедур. В этом разделе будут показаны три способа передачи структур в процедуры и функции иописаны их отличия. Рассмотрим процедуру, которая записывает в поле year(год изданиякниги) значение 2009. Передача по значению Если параметр процедуры объявлен как: void Year2009( Book b ) { b.year = 2009; } main() { Book b; Year2009 ( b ); } то при работе процедуры создается КОПИЯ этой структуры в стеке (специальной области памяти, где хранятся параметры и локальные переменные процедур и функций) и процедура работает с этой копией. Такой подход имеет два недостатка: 1) во-первых, процедура не изменяет поля структуры в вызывающей программе; это значит, что в нашем случае задача решается неверно; 2) во-вторых, самое главное – структуры могут быть достаточно больших размеров, и создание новой копии может привести к нехватке места в стеке, где создаются локальные переменные. Передача по ссылке Поэтому чаще всего параметры-структуры передаются в процедуры и функции по ссылке (при объявлении за именем типа структуры ставят знак &). void Year2009( Book &b ) { b.year = 2009; } В этом случае фактически в процедуру передается адрес структуры и процедура работает с темже экземпляром, что и вызывающая программа. При этом все изменения, сделанные в процедуре, остаются в силе. Работа со структурой-параметром внутри процедуры и вызов этой процедуры из основной программы никак не отличаются от предыдущего варианта. Передача по адресу Передача по ссылке возможна только при использовании языка Си++ (она не входит в стандарт языка Си). Тем не менее, в классическом Си можно передать в качестве параметраадрес структуры. При этом обращаться к полям структуры внутри процедуры надо через оператор ->. void Year2009( Book *b ) // параметр – адрес структуры { b->year = 2009; // обращение по адресу (через указатель) } main() { Book b; Year2009 ( &b ); // передается адрес структуры } Сортировка по ключу Поскольку в структуры входит много полей, возникает вопрос: а как их сортировать? Это зависит от конкретной задачи, но существуют общие принципы, о которых мы и будем говорить.Для сортировки выбирается одно из полей структуры, оно называется ключевым полемили просто ключом. Структуры расставляются в определенном порядке по значению ключевогополя, содержимое всех остальных полей игнорируется.И еще одна проблема – при сортировке элементы массива меняются местами. Структурымогут быть достаточно больших размеров, и копирование этих структур будет занимать оченьмного времени. ПоэтомуСортировку массива структур обычно выполняют по указателям. Мы уже использовали этот прием для сортировки массива символьных строк. В памяти формируется массив указателей p, и сначала они указывают на структуры в порядке расположенияих в массиве, то есть указатель p[i] указывает на структуру с номером i. Затем остается только расставить указатели так, чтобы ключевые поля соответствующих структур были отсортированы в заданном порядке. Для решения задачи удобно объявить новый тип данных PBook – указатель на структуру Book. typedef Book *PBook; Пример ниже показывает сортировку массива структур по году выпуска книг. Основной алгоритм сортировки массива указателей заложен в процедуру SortYear, которая имеет два параметра – массив указателей и число структур. Здесь используется «метод пузырька». void SortYear ( PBook p[], int n ) { int i, j; PBook temp; // вспомогательный указатель for ( i = 0; i < n-1; i ++ ) // сортировка for ( j = n-2; j >= i; j -- ) if ( p[j+1]->year < p[j]->year ) // еслиуказателистоят { // неправильно, то... temp = p[j]; // переставить их p[j] = p[j+1]; p[j+1] = temp; } } Основная программа, которая выводит на экран информацию о книгах в порядке их выхода,выглядит примерно так: #include const int N = 20; struct Book { // новый тип данных - структура char author[40]; char title[80]; int year; int pages; }; typedef Book *PBook; // новый тип данных – указатель на структуру // здесь надо расположить процедуру SortYear main() { Book B[N]; // массив структур PBook p[N]; // массив указателей // здесь нужно заполнить структуры for ( i = 0; i < N; i ++ ) // начальная расстановка указателей p[i] = &B[i]; SortYear ( p, N ); // сортировка по указателям for ( i = 0; i < N; i ++ ) // цикл по всем структурам printf("%d: %s\n", // вывод через указатели p[i]->year, p[i]->title); } |