- Lektsia - бесплатные рефераты, доклады, курсовые работы, контрольные и дипломы для студентов - https://lektsia.info -

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



Динамическое распределение памяти в языке С: функции malloc() и free()

Язык С не содержит операторов new или delete. Вместо них в С используются библиотечные функции, предназначенные для выделения и освобождения памяти. В целях совместимости C++ по-прежнему поддерживает С-систему динамического распределения памяти и не зря: в С++-программах все еще используются С-ориентированные средства динамического распределения памяти. Поэтому им стоит уделить внимание.

Ядро С-системы распределения памяти составляют функции malloc() и free(). Функция malloc() предназначена для выделения памяти, а функция frее() — для ее освобождения. Другими словами, каждый раз, когда с помощью функции malloc() делается запрос, часть свободной памяти выделяется в соответствии с этим запросом. При каждом вызове функции frее() соответствующая область памяти возвращается системе. Любая программа, которая использует эти функции, должна включать заголовок <cstdlib>.

Функция malloc() имеет такой прототип,

void *malloc(size_t num_bytes);

Здесь num_bytes означает количество байтов запрашиваемой памяти. (Тип size_t представляет собой разновидность целочисленного типа без знака.) Функция malloc() возвращает указатель типа void, который играет роль обобщенного указателя. Чтобы из этого обобщенного указателя получить указатель на нужный вам тип, необходимо использовать операцию приведения типов. В результате успешного вызова функция malloc() возвратит указатель на первый байт области памяти, выделенной из "кучи". Если для удовлетворения запроса свободной памяти в системе недостаточно, функция malloc() возвращает нулевой указатель.

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

void free(void *ptr);

Здесь параметр ptr представляет собой указатель на память, ранее выделенную с помощью функции malloc(). Никогда не следует вызывать функцию free() с недействительным аргументом; это может привести к разрушению списка областей памяти, подлежащих освобождению.

Использование функций malloc() и free() иллюстрируется в следующей программе.

// Демонстрация использования функций malloc() и free().

#include <iostream>

#include <cstdlib>

using namespace std;

Int main()

{

 int *i;

 double *j;

 i = (int *) malloc(sizeof(int));

 if(!i) {

  cout << "Выделить память не удалось.";

  return 1;

 }

 j = (double *) malloc(sizeof(double));

 if(! j ) {

  cout << "Выделить память не удалось.";

  return 1;

 }

 *i = 10;

 *j = 100.123;

 cout << *i << ' ' << *j;

 // Освобождение памяти.

 free (i);

 free (j);

 return 0;

}

Несмотря на то что функции malloc() и fгее() — полностью пригодны для динамического распределения памяти, есть ряд причин, по которым в C++ определены собственные средства динамического распределения памяти. Во-первых, оператор new автоматически вычисляет размер выделяемой области памяти для заданного типа, т.е. вам не нужно использовать оператор sizeof, а значит, налицо экономия в коде и трудовых затратах программиста. Но важнее то, что автоматическое вычисление не допускает выделения неправильного объема памяти. Во-вторых, С++-оператор new автоматически возвращает корректный тип указателя, что освобождает программиста от необходимости использовать операцию приведения типов. В-третьих, используя оператор new, можно инициализировать объект, для которого выделяется память. Наконец, как будет показано ниже в этой книге, программист может создать собственные версии операторов new и delete.

И последнее. Из-за возможной несовместимости не следует смешивать функции malloc() и free() с операторами new и delete в одной программе.

Сводная таблица приоритетов С++-операторов

В табл. 9.2 показан приоритет выполнения всех С++-операторов (от высшего до самого низкого). Большинство операторов ассоциированы слева направо. Но унарные операторы, операторы присваивания и оператор "?" ассоциированы справа налево. Обратите внимание на то, что эта таблица включает несколько операторов, которые мы пока не использовали в наших примерах, поскольку они относятся к объектно-ориентированному программированию (и описаны ниже).


Глава 10: Структуры и объединения

В языке C++ определено несколько составных типов данных, т.е. типов, которые состоят из двух или более элементов. С одним из составных типов — массивом — вы уже знакомы. С двумя другими — структурами и объединениями — вы познакомитесь в этой главе, а знакомство с еще одним типом — классом — мы отложим до главы 11. И хотя структура и объединение предназначены для удовлетворения различных потребностей программистов, оба они представляют собой удобные средства управления группами связанных переменных. При этом важно понимать, что создание структуры (или объединения) означает создание нового определенного программистом типа данных. Сама возможность создания собственных типов данных является признаком могущества языка C++.

В C++ структуры и объединения имеют как объектно-ориентированные, так и не объектно-ориентированные атрибуты. В этой главе рассматриваются только последние. Об объектно-ориентированных и их свойствах речь пойдет в следующей главе после введения понятия о классах и объектах.

Структуры

Структура — это группа связанных переменных.

В C++ структура представляет собой коллекцию объединенных общим именем переменных, которая обеспечивает удобное средство хранения родственных данных в одном месте. Структуры — это совокупные типы данных, поскольку они состоят из нескольких различных, но логически связанных переменных. По тем же причинам их иногда называют составными или конгломератными типами данных.

Прежде чем будет создан объект структуры, должен быть определен ее формат. Это делается посредством объявления структуры. Объявление структуры позволяет понять, переменные какого типа она содержит. Переменные, составляющие структуру, называются ее членами. Члены структуры также называют элементами или полями.

Член структуры — это переменная, которая является частью структуры.

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

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

struct inv_type {

 char item[40]; // наименование товара

 double cost; // стоимость

 double retail; // розничная цена

 int on_hand; // имеющееся в наличии количество

 int lead_time; // число дней до пополнения запасов

};

Имя структуры — это ее спецификатор типа.

Обратите внимание на то, что объявление завершается точкой с запятой. Дело в том, что объявление структуры представляет собой инструкцию. Именем типа структуры здесь является inv_type. Другими словами, имя inv_type идентифицирует конкретную структуру данных и является ее спецификатором типа.

В предыдущем объявлении в действительности не было создано ни одной переменной. Был определен лишь формат данных. Чтобы с помощью этой структуры объявить реальную переменную (т.е. физический объект), нужно записать инструкцию, подобную следующей.

inv_type inv_var;

Вот теперь объявляется структурная переменная типа inv_type с именем inv_var. Помните: определяя структуру, вы определяете новый тип данных, но он не будет реализован до тех пор, пока вы не объявите переменную того типа, который уже реально существует.

При объявлении структурной переменной C++ автоматически выделит объем памяти, достаточный для хранения всех членов структуры. На рис. 10.1 показано, как переменная inv_var будет размещена в памяти компьютера (в предположении, что double-значение занимает 8 байт, а int-значение — 4).

Одновременно с определением структуры можно объявить одну или несколько переменных, как показано в этом примере.

struct inv_type {

 char item[40]; // наименование товара

 double cost; // стоимость

 double retail; // розничная цена

 int on_hand; // имеющееся в наличии количество

 int lead_time; // число дней до пополнения запасов

} inv_varA, inv_varB, inv_varC;

Этот фрагмент кода определяет структурный тип inv_type и объявляет переменные inv_varA, inv_varB и inv_varC этого типа. Важно понимать, что каждая структурная переменная содержит собственные копии членов структуры. Например, поле cost структуры inv_varA изолировано от поля cost структуры inv_varB. Следовательно, изменения, вносимые в одно поле, никак не влияют на содержимое другого поля.

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

struct {

 char item[40]; // наименование товара

 double cost; // стоимость

 double retail; // розничная цена

 int on_hand; // имеющееся в наличии количество

 int lead_time; // число дней до пополнения запасов

} temp;

Этот фрагмент кода объявляет одну переменную temp в соответствии с предваряющим ее определением структуры.

Ключевое слово struct означает начало объявления структуры.