Главная » Статьи » Мои статьи » С++ для начинающих

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

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

До сих пор во всех наших примерах использовалось статическое выделение памяти. Скажем, определение переменной ival

int ival = 1024;

заставляет компилятор выделить в памяти область, достаточную для хранения переменной типа int, связать с этой областью имя ival и поместить туда значение 1024. Все это делается на этапе компиляции, до выполнения программы.

С объектом ival ассоциируются две величины: собственно значение переменной, 1024 в данном случае, и адрес той области памяти, где хранится это значение. Мы можем обращаться к любой из этих двух величин. Когда мы пишем:

int ival2 = ival + 1;

то обращаемся к значению, содержащемуся в переменной ival: прибавляем к нему 1 и инициализируем переменную ival2 этим новым значением, 1025. Каким же образом обратиться к адресу, по которому размещена переменная?

С++ имеет встроенный тип "указатель”, который используется для хранения адресов объектов. Чтобы объявить указатель, содержащий адрес переменной ival, мы должны написать:

int *pint; // указатель на объект типа int

Существует также специальная операция взятия адреса, обозначаемая символом &. Ее результатом является адрес объекта. Следующий оператор присваивает указателю pint адрес переменной ival:

int *pint;

pint = &ival; // pint получает значение адреса ival

Мы можем обратиться к тому объекту, адрес которого содержит pint (ival в нашем случае), используя операцию разыменования, называемую также косвенной адресацией. Эта операция обозначается символом *. Вот как можно косвенно прибавить единицу к ival, используя ее адрес:

*pint = *pint + 1; // неявно увеличивает ival

Это выражение производит в точности те же действия, что и

ival = ival + 1; // явно увеличивает ival

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

Основные отличия между статическим и динамическим выделением памяти таковы:

·                   статические объекты обозначаются именованными переменными, и действия над этими объектами производятся напрямую, с использованием их имен. Динамические объекты не имеют собственных имен, и действия над ними производятся косвенно, с помощью указателей;

·                   выделение и освобождение памяти под статические объекты производится компилятором автоматически. Программисту не нужно самому заботиться об этом. Выделение и освобождение памяти под динамические объекты целиком и полностью возлагается на программиста. Это достаточно сложная задача, при решении которой легко наделать ошибок. Для манипуляции динамически выделяемой памятью служат операторы new и delete.

Оператор new имеет две формы. Первая форма выделяет память под единичный объект определенного типа:

int *pint = new int(1024);

Здесь оператор new выделяет память под безымянный объект типа int, инициализирует его значением 1024 и возвращает адрес созданного объекта. Этот адрес используется для инициализации указателя pint. Все действия над таким безымянным объектом производятся путем разыменовывания данного указателя, т.к. явно манипулировать динамическим объектом невозможно.

Вторая форма оператора new выделяет память под массив заданного размера, состоящий из элементов определенного типа:

int *pia = new int[4];

В этом примере память выделяется под массив из четырех элементов типа int. К сожалению, данная форма оператора new не позволяет инициализировать элементы массива.

Некоторую путаницу вносит то, что обе формы оператора new возвращают одинаковый указатель, в нашем примере это указатель на целое. И pint, и pia объявлены совершенно одинаково, однако pint указывает на единственный объект типа int, а pia – на первый элемент массива из четырех объектов типа int.

Когда динамический объект больше не нужен, мы должны явным образом освободить отведенную под него память. Это делается с помощью оператора delete, имеющего, как и new, две формы – для единичного объекта и для массива:

// освобождение единичного объекта

delete pint;

// освобождение массива

delete[] pia;

Что случится, если мы забудем освободить выделенную память? Память будет расходоваться впустую, она окажется неиспользуемой, однако возвратить ее системе нельзя, поскольку у нас нет указателя на нее. Такое явление получило специальное название утечка памяти. В конце концов программа аварийно завершится из-за нехватки памяти (если, конечно, она будет работать достаточно долго). Небольшая утечка трудно поддается обнаружению, но существуют утилиты, помогающие это сделать.

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

Упражнение 2.3

Объясните разницу между четырьмя объектами:

(a) int ival = 1024;

(b) int *pi = &ival;

(c) int *pi2 = new int(1024);

(d) int *pi3 = new int[1024];

Упражнение 2.4

Что делает следующий фрагмент кода? В чем состоит логическая ошибка? (Отметим, что операция взятия индекса ([]) правильно применена к указателю pia. Объяснение этому факту можно найти в разделе 3.9.2.)

int *pi = new int(10);

int *pia = new int[10];

 

while ( *pi < 10 ) {

  pia[*pi] = *pi;

  *pi = *pi + 1;

}

 

delete pi;

delete[] pia;

Категория: С++ для начинающих | Добавил: Vayolet (28.05.2010)
Просмотров: 13376 | Комментарии: 7 | Теги: Динамическое выделение памяти и ука, учебник по С++ | Рейтинг: 5.0/1
Всего комментариев: 5
5 ankasvibla  
0
Приветствую Вас. Ходят слухи что вы раскручиваете интеренет - ресурс. нашел нужный сайт.
Там раздают 2000 входящих ссылок на интеренет сайт - практически совсем на халяву и оплата только после размещения.

propisun.ru


купить качественные вечные ссылки

4 ankasvibla  
0
Привет. Ходят слухи что ты занимаешься раскруткой персонального веб - портала. Случайно встретил полезный веб - ресурс.
Там любому раздают 2000 ссылок на интеренет - портал - практически совсем даром и оплата только после размещения.

www.propisun.ru


создание и раскрутка сайта цена

3 Arcan  
0
Логическая ошибка в том, что в первой строчке кода инициализируем значение 10 безымянному объекту, а в цикле while берем это значение и сравниваем с 10 (пока 10<10 в этом ошибка). Если в первой строчке инициализировать объекту значение 0, то программа будет работать как надо. int *pi = new int(0); Дальше происходит инициализация массива (т.е. его элементы принимают значения от 0 до 10).
Вот полный рабочий код программы:
#include <iostream>

using namespace std;

int main()
{
setlocale(LC_ALL,"RUS");

int *pi = new int(0);

int *pia = new int[10];
while (*pi<10) {
pia[*pi] = *pi;
*pi = *pi + 1;


}
for (int i=0; i<10; i++){
cout << pia[i] <<endl; // смотрим содержимое массива
}
delete pi;
delete[] pia;

system("PAUSE");
return EXIT_SUCCESS;
}

2 Странник  
0
Благодарю. Завязал с С несколько лет назад, а на днях потребовалось реанимировать. Первое, что сделал, "поплыл" с указателями. Теперь надеюсь не заблудиться.
____________________________________________________________________________

Плыл с указателями С, а заплыл за буки С++ biggrin


1 Алексей  
0
Благодарю. Завязал с С несколько лет назад, а на днях потребовалось реанимировать. Первое, что сделал, "поплыл" с указателями. Теперь надеюсь не заблудиться.

biggrin


Имя *:
Email *:
Код *: