Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
DOROGOVA.pdf
Скачиваний:
245
Добавлен:
05.06.2015
Размер:
853.4 Кб
Скачать

Вопросы для самопроверки

1.Какие разновидности пользовательских типов данных Вы знаете?

2.Дайте определение структурного типа данных.

3.Приведите пример использования структур.

4.Назовите этапы создания структурного типа данных.

5.Чем отличается создание структурного типа данных от создания структурного объекта данных?

6.Приведите пример создание структуры с помощью оператора struct.

7.Приведите пример создание структуры с помощью оператора typedef.

8.Покажите разницу между использованием структур, созданных с помощью ключевых слов struct и

typedef.

9.Назовите два оператора доступа к элементам структур, приведите примеры.

10.Допускается ли вложение одной структуры в другую? Если да, то, как осуществляется доступ к элементам вложенной структуры.

11.Допустимо ли использовать операцию присваивания целиком к структурным объектам? Если да, то, при каких условиях?

12.Допустимо ли использовать операции сравнения целиком к структурным объектам? Если да, то, при каких условиях?

13.С помощью какого оператора можно определить реальный размер структуры?

14.Дайте определение понятию "битовые поля", для каких целей они используются?

15.Приведите пример определения битовых полей.

16.Дайте определение понятию "объединения", для каких целей оно используются?

17.Приведите пример определения объединения.

18.Может ли массив выступать в роли элемента объединения?

19.Для чего используются "перечисления"?

20.Приведите пример использования перечисления.

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

10. Динамическая работа с памятью

Все объекты, размещенные в памяти - переменные, массивы, структуры, указатели и тому подобное, можно разделить на две категории:

Объекты, создаваемые до начала работы программы, при компиляции.

Объекты, создаваемые в процессе работы программы.

Опервых говорят как о статических, постоянно размещенных в памяти компьютера объектах, о вторых

-как о динамических, или временных объектах, создаваемых и уничтожаемых в процессе работы программы.

К статическим объектам можно отнести глобальные переменные и static-переменные.

К динамическим объектам, с которыми мы уже успели познакомиться, относятся локальные переменные функций и блоков операторов.

Ранее мы научились работать со статическими массивами, создавали их, задавая размер либо явно,

либо списком инициализации, в обоих случаях размер массива был известен и задавался до начала выполнения программы, при компиляции.

На практике встречается очень много задач, когда заранее нельзя определить, сколько элементов массива потребуется для работы, например:

найти пересечение 2-х массивов;

найти элементы массива, значения которых больше заданного;

базы данных для хранения информации;

и тому подобное.

Такие задачи можно решить, создавая массивы или другие структуры данных "с запасом", но этот способ, во-первых, неэкономно использует память, и, во-вторых, не всегда решает поставленную задачу.

Правильнее в такой ситуации создать массив нужного размера динамически - непосредственно во время работы программы, когда этот размер уже известен.

Как статические, так и динамические объекты размещаютися в памяти компьютера в строгой последовательности, определяемой операционной системой компьютера и компилятором языка программирования. Распределение памяти в программах, написанных на языке С, представлено на рис. 10.1.

(Верхние адреса памяти)

Локальные переменные и константы.

СТЕК.

 

 

 

СВОБОДНАЯ ПАМЯТЬ.

Динамическая память (Куча).

 

 

Глобальные переменные и

Фиксированное место в памяти

константы.

(на все время работы программы).

 

 

ПРОГРАММА

 

ПОЛЬЗОВАТЕЛЯ

 

(Нижние адреса памяти).

 

 

 

Рис. 10.1. Распределение памяти компьютера

Операционная система позволяет получать (а также возвращать) память динамически ИЗ КУЧИ во время выполнения программы.

Для работы с динамической памятью в С предусмотрено четыре функции из библиотеки stdlib.h:

malloc(), calloc() - захватывают память или выделяют память из кучи;

realloc() - позволяет изменять (как правило, увеличивать) размер уже выделенной памяти без потери

её

содержимого;

free() - освобождает уже ненужную память (возвращает память в кучу).

Функция malloc() захватывает область памяти, при этом размер в байтах указывается в качестве аргумента, malloc() возвращает указатель на захваченную память, то есть, ее адрес. При этом выделяемая память не инициализируется, то есть не очищается.

Пример: создать динамический массив из ста символов, а затем заполнить его нулями: #include <stdlib.h>

main()

{

int i; char *p;

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

/* Резервируем 100 байтов под массив char */ p = malloc(100);

for (i=0; i<100; i++) p[i] = 0;

return 0;

}

Если нам нужен массив не char, а например, int, то придется определить размер памяти в байтах с помощью оператора sizeof(), это можно сделать прямо при вызове функции:

/* Резервируем место под 100 элементов int */ p = malloc( 100 * sizeof(int) );

Функция calloc(), не только захватывает память, но и очищает ее (заполняет байты нулями), у нее два аргумента количество элементов и размер элемента в байтах:

#include <stdlib.h> main()

{ int i; char *сp; int *ip;

/* Резервируем и очищаем 100 байтов под массив char */

сp = сalloc(100, 1 );

/*Резервируем и очищаем 100 элементов int*/ ip = сalloc(100, sizeof(*ip) );

return 0;

}

Функция calloc(nelm, elmsize) выделит столько же байтов, сколько и malloc(nelm*elmsize) и, кроме того, очистит выделенную память.

В примерах выше мы пользовались динамической памятью только для создания массивов, но таким же образом можно выделять память и под структуры, переменные встроенных типов и тому подобное.

На самом деле все эти функции работают с "абстрактной" памятью, и им абсолютно безразлично, как программист ее будет использовать. А тот указатель (точнее тип указателя), в который вы записываете адрес области, лишь помогает программе интерпретировать эту область нужным вам образом. Поставьте вместо указателя на int указатель на структуру - и вы будете работать с динамически созданной структурой или массивом структур:

#include <stdlib.h> #include "compleh.h"

struct COMPLEX { double re, im; }; void main()

{ struct COMPLEX *ptr; ptr=malloc(sizeof(struct COMPLEX)); ptr->re = 1.0;

ptr->im=0.0;

}

Функция realloc() позволяет изменять размер выделенного участка динамической памяти.

После создния динамического массива вы можете "увеличить" или уменьшить его размер. #include <stdlib.h>

void main() { int *ip;

/* Резервируем 100 элементов int */ ip = malloc(100*sizeof(*ip) );

...; // не хватило - наращиваем до 200 элементов

ip = realloc(ip, 200*sizeof(*ip) );

...; /* Нам достаточно 150 элементов, убираем 50 лишних */

ip = realloc(ip, 150*sizeof(*ip) );

...; return 0;

}

У функции realloc() два параметра:

указатель на уже имеющуюся область динамической памяти;

новый размер этой области;

функция также использует возвращаемое значение указатель на захваченную область нужного размера

вбайтах.

Рассмотрим три особенности realloc():

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

1.Функция realloc() может изменять размер только динамического массива. В качестве первого аргумента ей можно передать только указатель, который, как в примере выше, был получен от malloc(), calloc() или от предыдущего вызова самой realloc(). Если вы попытаетесь передать ей адрес статического массива, компилятор выдаст ошибку:

char s[100]; char *p; char *p1;

p=malloc(100);

p1=realloc(p, 150); // так правильно p1=realloc(s,150); //а так делать нельзя!!!

2.Обратим внимание на содержимое новой области. Если вы увеличиваете размер массива с помощью realloc(), то содержимое исходного массива у вас сохранится., новые же элементы будут содержатьнепредсказуемые значения или "информационный мусор".

int *ip;

ip=malloc(2*sizeof(*ip)); // Создали массив из 2-х целых ip[0]=0;

ip[1]=1;

ip=realloc(ip, 3*sizeof(*ip)); // Добавляем еще один элемент.

ip = realloc(ip, sizeof(*ip)); // А теперь уберем два элемента

Естественно, вместе с элементами ip[1] и ip[2] мы потеряли и их содержимое.

3. И наконец надо учитывать, что когда вы пользуетесь realloc(), система не изменяет размер самой области, с которой вы работали, а создает для вас её копию, а исходную область освобождает. А это означает, что содержимое массива вы сохраняете, а вот адрес этого массива чаще всего становится другим, поэтому передав realloc() указатель в качестве первого аргумента, вы должны забыть о нем, и в дальнейшем

использовать только тот новый указатель, который получите от realloc():

 

char *p1, *p2;

 

 

 

p1=malloc(100);

 

 

 

p2 = p1; // Сохраняем копию указателя в p2

 

/*После realloc() p1 скорее всего

поменяется, поэтому пользоваться

старой копией p2 нельзя,

память под этим адресом уже освобождена в realloc()*/

 

p1=realloc(p1, 1000);

 

 

p1[0] = 'c';

// Правильно

 

 

p2[0] = 'c';

// Ошибка. Эта память уже не наша

 

Во всех примерах, иллюстрирующих выделение памяти, предполагалось, что программа эту память получает. Однако ресурсы любого компьютера ограничены, и может оказаться, что в системе не окажется нужного количества свободной памяти. В этом случае все три функции сообщат вам об ошибке, вернув NULL. Так что полезно после вызова любой из этих функций проверить, не возникло ли ошибки при выделении памяти:

char *p;

//Хотим получить гигабайт p = malloc(1024*1024*1024); // Проверяем, удлось ли это? if (p == NULL)

{ printf("Нет нужного объема памяти \n"); exit(1);

};

Если вы используете в программе динамический массив, рано или поздно наступает момент, когда он перестает быть нужен. Если этот момент совпадает с завершением задачи, можно особо не беспокоиться на этот счет - современные системы, снимая задачу с выполнения, заодно и освобождают отведенную ей память. Однако чаще случается, что массив уже не нужен, а программа продолжает работать. И в этом случае совсем не вредно освободить выделенную память - во-первых, другим задачам может пригодиться, а во вторых, вашей же задаче может не хватить памяти для другого массива.

Для того, чтобы освободить динамическую память, надо вызвать функцию free(), передав ей в аргументе тот указатель, который вы получили от malloc(), calloc() или realloc(). После вызова free() этим указателем уже нельзя будет пользоваться.

int *p;

p = malloc(5 * sizeof(*p)); // Создаем динамический массив p[0] = 1;

free(p); //Освобождаем память

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]