Прием проект спутниковая связь. Спутниковый телефон: возможности и характеристики

Прием проект спутниковая связь. Спутниковый телефон: возможности и характеристики

Динамическое выделение памяти

Основные проблемы применения

Нулевой указатель

Нулевой указатель − это указатель, хранящий специальное значение, используемое для того, чтобы показать, что данная переменная-указатель не ссылается (не указывает) ни на какой объект. В различных языках программирования представлен различными константами.

·В языках C# и Java: null

·В языках Си и C++: 0 или макрос NULL. Кроме того, в стандарте C++11 для обозначения нулевого указателя предложено новое ключевое слово nullptr

·В языках Паскаль и Ruby: nil

·В языке Компонентный Паскаль:NIL

·В языке Python: None

Указателями сложно управлять. Достаточно легко записать в указатель неправильное значение, что может вызвать трудно воспроизводимую ошибку. Например, вы случайно поменяли адрес указателя в памяти, или неправильно выделили под информацию память и тут вас может ожидать сюрприз: другая очень важная переменная, которая используется только внутри программы будет перезаписана. В таком случае вам будет сложно воспроизвести баг. Нелегко будет и понять, где именно находится ошибка. И не всегда тривиально будет её устранить (иногда придётся переписать существенную часть программы).

Для решения части проблем есть методы предохранения и страховки:

Изучив указатели в языке Си, мы открыли для себя возможности динамического выделения памяти. Что это значит? Это значит то, что при динамическом выделении памяти, память резервируется не на этапе компиляции а на этапе выполнения программы. И это дает нам возможность выделять память более эффективно, в основном это касается массивов. С динамическим выделением память, нам нет необходимости заранее задавать размер массива, тем более, что не всегда известно, какой размер должен быть у массива. Далее рассмотрим каким же образом можно выделять память.

Функция malloc() определена в заголовочном файле stdlib.h, она используется для инициализации указателей необходимым объемом памяти. Память выделяется из сектора оперативной памяти доступного для любых программ, выполняемых на данной машине. Аргументом функции malloc() является количество байт памяти, которую необходимо выделить, возвращает функция - указатель на выделенный блок в памяти. Функция malloc() работает также как и любая другая функция, ничего нового.

Так как различные типы данных имеют разные требования к памяти, мы как-то должны научиться получить размер в байтах для данных разного типа. Например, нам нужен участок памяти под массив значений типа int - это один размер памяти, а если нам нужно выделить память под массив того же размера, но уже типа char - это другой размер. Поэтому нужно как-то вычислять размер памяти. Это может быть сделано с помощью операции sizeof(), которая принимает выражение и возвращает его размер. Например, sizeof(int) вернет количество байтов, необходимых для хранения значения типа int. Рассмотрим пример:


Яндекс.Директ


#include int *ptrVar = malloc(sizeof(int));

В этом примере, встроке 3 указателю ptrVar присваивается адрес на участок памяти, размер которого соответствует типу данных int. Автоматически, этот участок памяти становится недоступным для других программ. А это значит, что после того, как выделенная память станет ненужной, её нужно явно высвободить. Если же память не будет явно высвобождена, то по завершению работы программы, память так и не освободится для операционной системы, это называется утечкой памяти. Также можно определять размер выделяемой памяти, которую нужно выделить передавая пустой указатель, вот пример:



Как видите, в такой записи есть одна очень сильная сторона, мы не должны вызывать функцию malloc() с использованиемsizeof(float). Вместо этого мы передали в malloc() указатель на тип float, в таком случае, размер выделяемой памяти автоматически определится сам!

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


float *ptrVar; /* . . . сто строк кода */ . . . ptrVar = malloc(sizeof(*ptrVar));

Если бы вы использовали конструкцию выделения памяти с операцией sizeof(), то вам бы пришлось находить в коде определение указателя, смотреть его тип данных и уже потом вы бы смогли правильно выделить память.

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

char place = "Залив Свиной печенки";

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

Или мы можем быть более конкретны и запросить определенный объем памяти:

int plates;

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

Язык Си не останавливается на этом. Он позволяет вам распределять дополнительную память во время работы программы. Предположим, например, вы пишете диалоговую программу и не знаете заранее, сколько данных вам придется вводить. Можно выделить нужный вам (как вы считаете) объем памяти, а затем, если понадобится, потребовать еще. На рис. 15.5 дан пример, в котором используется функция malloc() , чтобы сделать именно это. Кроме того, обратите внимание на то, как такая программа применяет указатели.

/* добавляет память, если необходимо */

#include

#define STOP " " /* сигнал прекращения ввода */

#define BLOCK 100 /* байты памяти */

#define LIM 40 /* предельная длина вводимой строки */

#define MAX 50 /* максимальное число вводимых строк */

#define DRAMA 20000 /* большая задержка времени */

char store; /* исходный блок памяти */

char symph; /* приемник вводимых строк */

char *end; /* указывает на конец памяти */

char *starts; /* указывает на начала строк */

int index = 0; /* количество вводимых строк */

int count; /* счетчик */

char *malloc(); /* распределитель памяти */

starts = store;

end = starts + BLOCK - 1;

puts(" Назовите несколько симфонических оркестром.");

puts(" Вводите по одному: нажмите клавишу [ввод] в начале");

puts(" строки для завершения вашего списка. Хорошо, я готова.");

while(strcmp(fgets(symph, LIM, stdin), STOP) != 0 && index < MAX)

{ if(strlen(symph) > end - starts)

{ /* действия при недостатке памяти для запоминания вводимых данных*/

puts(" Подождите секунду. Я попробую найти дополнительную память.");

end = starts + BLOCK - 1;

for(count = 0; count < DRAMA; count++);

puts(" Нашла немного!"); }

strcpy (starts , symph);

starts = starts + strlen(symph) + 1;

if(++index < MAX)

printf("Этo %d. Продолжайте, если хотите.n", index); }

puts(" Хорошо, вот что я получила:");

for(count = 0; count < index; count ++)

puts(starts);

РИС. 15.5. Программа, добавляющая память по требованию.

Вот образец работы программы:

Назовите несколько симфонических оркестров оркестров.

Вводите их по одному; нажмите клавишу [ввод] в начале

строки для завершения нашего списка. Хорошо, я готова.

Сан-франциский симфонический.

Это 1. Продолжайте, если хотите.

Чикагский симфонический

Это 2. Продолжайте, если хотите.

Берлинский филармонический

Это 3. Продолжайте, если хотите.

Московский камерный

Это 4. Продолжайте, если хотите. Лондонский симфонический

Это 5. Продолжайте, если хотите. Венский филармонический

Подождите секунду. Я попробую найти дополнительную память.

Нашла немного!

Это 6. Продолжайте, если хотите.

Питтсбургский симфонический

Это 7. Продолжайте, если хотите.

Хорошо, вот что я получила:

Сан-францизкий симфонический

Чикагский симфонический

Берлинский филармонический

Московский камерный

Лондонский симфонический

Венский филармонический

Питтсбургский симфонический

Сначала давайте посмотрим, что делает функция malloc() . Она берет аргумент в виде целого без знака, которое представляет количество требуемых байтов памяти. Так, malloc(BLOCK) требует 100 байт. Функция возвращает указатель на тип char в начало нового блока памяти. Мы использовали описание

char *malloc();

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

starts = malloc(BLOCK);

Хорошо, давайте теперь рассмотрим проект программы, заключающийся в том, чтобы запомнить все исходные строки подряд в большом массиве store . Мы хотим использовать starts для ссылки на начало первой строки, starts[l] - второй строки и т. д. На промежуточном этапе программа вводит строку в массив symph . Мы использовали fgets() вместо gets() , чтобы ограничить входную строку длиной массива symph .

РИС. 15.6. Последовательные строки symph, записанные в массив store.

Прежде чем копировать symph в store , мы должны проверить, достаточно ли для нее оставшегося места. Указатель end ссылается на конец памяти, а текущее значение starts ссылается на начало неиспользованной памяти. Таким образом, мы можем сравнить разницу между этими двумя указателями с длиной symph и определить, достаточно ли осталось памяти.

Если места недостаточно, вызываем malloc() , чтобы подготовить дополнительную память. Мы устанавливаем starts на начало нового блока памяти, a end - на конец нового блока. Заметим, что у нас нет имени этой новой памяти. Она не является, например, расширением store . У нас есть только обозначения указателей, ссылающихся на новую область памяти.

Когда программа работает, на каждую новую строку ссылается элемент массива указателей starts . Некоторые строки находятся в store , другие - в одной или нескольких новых областях памяти.

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

Таким образом используется mаllос() . Но предположим, что вы хотите работать с памятью типа int , а не char . Можете и здесь использовать mаllос() . Вот как это делается:

char *malloc(); /* по-прежнему описываем как указатель на char */

int *newmem;

newmem = (int *) malloc(l00); /* используем операцию приведения типа */

Снова требуется 100 байт. Операция приведения типа преобразует значение, возвращенное указателем на тип char , в указатель на тип int . Если, как в нашей системе, int занимает два байта памяти, это значит, что newmem + 1 будет увеличивать указатель на два байта, т. е. передвигать его к следующему целому. Это также означает, что 100 байт можно использовать для запоминания 50 целых чисел.

Другую возможность распределения памяти дает нам применение функции саllос() :

char *calloc();

long *newmem;

newmem = (long *) calloc(100, sizeof(long));

Подобно malloc() функция саllос() возвращает указатель на char . Нужно использовать оператор приведения типа, если вы хотите запомнить другой тип. Эта новая функция имеет два аргумента, и оба они должны быть целыми без знака. Первый аргумент содержит количество требуемых ячеек памяти. Второй аргумент - размер каждой ячейки в байтах. В нашем случае long

error: