Главная страница

Электронный конспект по программированию часть 1. Программа на языке Си, так же как и на большинстве современных языков программирования, создается в два этапа


Скачать 32.35 Mb.
НазваниеПрограмма на языке Си, так же как и на большинстве современных языков программирования, создается в два этапа
АнкорЭлектронный конспект по программированию часть 1.doc
Дата04.09.2018
Размер32.35 Mb.
Формат файлаdoc
Имя файлаЭлектронный конспект по программированию часть 1.doc
ТипПрограмма
#24059
страница12 из 12
1   ...   4   5   6   7   8   9   10   11   12

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);

}
1   ...   4   5   6   7   8   9   10   11   12


написать администратору сайта