ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 30.03.2021

Просмотров: 343

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

 return 0;

}


int div_zero()

{

 cout << "Нельзя делить на нуль. \n";

 return 0;

}


Здесь, если значение переменной j не равно нулю, выполняется деление значения переменной i на значение переменной j, а результат присваивается переменной result. В противном случае вызывается обработчик ошибки деления на нуль div_zeго(), и переменной result присваивается нулевое значение.



Оператор "запятая"


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


var = (count=19, incr=10, count+1);

переменной count сначала присваивается число 19, переменной incr — число 10, а затем к значению переменной count прибавляется единица, после чего переменной var присваивается значение крайнего справа выражения, т.е. count+1, которое равно 20. Круглые скобки здесь обязательны, поскольку оператор "запятая" имеет более низкий приоритет, чем оператор присваивания.

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


#include <iostream>

using namespace std;

int main()

{

 int i, j;

 j = 10;

 i = (j++, j+100, 999+j);

 cout << i;

 return 0;

}


Эта программа выводит на экран число 1010. И вот почему: сначала переменной j присваивается число 10, затем переменная j инкрементируется до 11. После этого вычисляется выражение j+100, которое нигде не применяется. Наконец, выполняется сложение значения переменной j (оно по-прежнему равно 11) с числом 999, что в результате дает число 1010.

По сути, назначение оператора "запятая" — обеспечить выполнение заданной последовательности операций. Если эта последовательность используется в правой части инструкции присваивания, то переменной, указанной в ее левой части, присваивается значение последнего выражения из списка выражений, разделенных запятыми. Оператор "запятая" по его функциональной нагрузке можно сравнить со словом "и", используемым в фразе: "сделай это, и то, и другое...".


Несколько присваиваний "в одном"

Язык C++ позволяет применить очень удобный метод одновременного присваивания многим переменным одного и того же значения. Речь идет об объединении сразу нескольких присваиваний в одной инструкции. Например, при выполнении этой инструкции переменным count, incr и index будет присвоено число 10.


count = incr = index = 10;

Этот формат присвоения нескольким переменным общего значения можно часто встретить в профессионально написанных программах.



Использование ключевого слова sizeof

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


sizeof (type)


sizeof value

Оператор sizeof во время компиляции программы получает размер типа или значения.

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

Чтобы понять, как работает оператор sizeof, испытайте следующую короткую программу. Для многих 32-разрядных сред она должна отобразить значения 1, 4, 4 и 8.


// Демонстрация использования оператора sizeof.


#include <iostream>

using namespace std;

int main()

{

 char ch;

 int i;

 cout << sizeof ch << ' '; // размер типа char

 cout << sizeof i << ' '; // размер типа int

 cout << sizeof (float) << ' '; // размер типа float

 cout << sizeof (double) << ' '; // размер типа double

 return 0;

}


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

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


int nums[4];


cout << sizeof nums; // Будет выведено число 16.


Для 4-байтных значений типа int при выполнении этого фрагмента кода на экране отобразится число 16 (которое получается в результате умножения 4 байт на 4 элемента массива).

Оператор sizeof главным образом используется при написании кода, который зависит от размера С++-типов данных.



Динамическое распределение памяти с использованием операторов new и delete

Для С++-программы существует два основных способа хранения информации в основной памяти компьютера. Первый состоит в использовании переменных. Область памяти, предоставляемая переменным, закрепляется за ними во время компиляции и не может быть изменена при выполнении программы. Второй способ заключается в использовании C++-системы динамического распределения памяти. В этом случае память для данных выделяется по мере необходимости из раздела свободной памяти, который расположен между вашей программой (и ее постоянной областью хранения) и стеком. Этот раздел называется "кучей" (heap). (Расположение программы в памяти схематично показано на рис. 9.2.)

Система динамического распределения памяти — это средство получения программой некоторой области памяти во время ее выполнения.



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

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

Оператор new позволяет динамически выделить область памяти.

Язык C++ содержит два оператора, new и delete, которые выполняют функции по выделению и освобождению памяти. Приводим их общий формат.


переменная-указатель = new тип_переменной;


delete переменная-указатель;

Оператор delete освобождает ранее выделенную динамическую память.

Здесь элемент переменная-указатель представляет собой указатель на значение, тип которого задан элементом тип_переменной. Оператор new выделяет область памяти, достаточную для хранения значения заданного типа, и возвращает указатель на эту область памяти. С помощью оператора new можно выделить память для значений любого допустимого типа. Оператор delete освобождает область памяти, адресуемую заданным указателем. После освобождения эта память может быть снова выделена в других целях при последующем new-запросе на выделение памяти.

Поскольку объем "кучи" конечен, она может когда-нибудь исчерпаться. Если для удовлетворения очередного запроса на выделение памяти не существует достаточно свободной памяти, оператор new потерпит фиаско, и будет сгенерировано исключение. Исключение— это ошибка специального типа, которая возникает во время выполнения программы. В общем случае ваша программа должна обработать подобное исключение и по возможности выполнить действие, соответствующее конкретной ситуации. Если это исключение не будет обработано вашей программой, ее выполнение будет прекращено.

Такое поведение оператора new в случае невозможности удовлетворить запрос на выделение памяти определено стандартом C++. На такую реализацию настроены также все современные компиляторы, включая последние версии Visual C++ и C++ Builder. Однако дело в том, что некоторые более ранние компиляторы обрабатывают new-инструкции по-другому. Сразу после изобретения языка C++ оператор new при неудачном выполнении возвращал нулевой указатель. Позже его реализация была изменена так, чтобы в случае неудачи генерировалось исключение, как было описано выше.



Рассмотрим пример программы, которая иллюстрирует использование операторов new и delete.


#include <iostream>

using namespace std;

int main()

{

 int *p;

 p = new int; // Выделяем память для int-значения.

 *p = 20; // Помещаем в эту область памяти значение 20.

 cout << *р; // Убеждаемся (путем вывода на экран) в работоспособности этого кода.

 delete р; // Освобождаем память.

 return 0;

}


Эта программа присваивает указателю р адрес (взятой из "кучи") области памяти, которая будет иметь размер, достаточный для хранения целочисленного значения. Затем в эту область памяти помещается число 20, после чего на экране отображается ее содержимое. Наконец, динамически выделенная память освобождается.

Благодаря такому способу организации динамического выделения памяти оператор delete необходимо использовать только с тем указателем на память, который был возвращен в результате new-запроса на выделение памяти. Использование оператора delete с другим типом адреса может вызвать серьезные проблемы.


Инициализация динамически выделенной памяти

Используя оператор new, динамически выделяемую память можно инициализировать. Для этого после имени типа задайте начальное значение, заключив его в круглые скобки. Например, в следующей программе область памяти, адресуемая указателем p, инициализируется значением 99.


#include <iostream>

using namespace std;

int main()

{

 int *p;

 p = new int (99); // Инициализируем память числом 99.

 cout << *p; // На экран выводится число 99.

 delete p;

 return 0;

}


Выделение памяти для массивов

С помощью оператора new можно выделять память и для массивов. Вот как выглядит общий формат операции выделения памяти для одномерного массива:


переменная-указатель = new тип [размер];

Здесь элемент размер задает количество элементов в массиве.

Чтобы освободить память, выделенную для динамически созданного массива, используйте такой формат оператора delete:


delete [] переменная-указатель;

Здесь элемент переменная-указатель представляет собой адрес, полученный при выделении памяти для массива (с помощью оператора new). Квадратные скобки означают для C++, что динамически созданный массив удаляется, а вся область памяти, выделенная для него, автоматически освобождается.

Важно! Более старые С++-компиляторы могут требовать задания размера удаляемого массива, поскольку в ранних версиях C++ для освобождения памяти, занимаемой удаляемым массивом, необходимо было применять такой формат оператора delete:


delete [размер] переменная-указатель;

Здесь элемент размер задает количество элементов в массиве. Стандарт C++ больше не требует указывать размер при его удалении.

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


#include <iostream>

using namespace std;

int main()

{

 double *p;

 int i;

 p = new double [10]; // Выделяем память для 10-элементного массива


 //Заполняем массив значениями от 100 до 109.

 for(i=0; i<10; i++) р[i] = 100.00 + i;

 // Отображаем содержимое массива.

 for(i=0; i<10; i++) cout << p[i] << " ";

 delete [] p; // Удаляем весь массив.

 return 0;

}


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


Динамическое распределение памяти в языке С: функции 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 << "Выделить память не удалось.\n";

  return 1;

 }

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

 if(! j ) {

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

  return 1;

 }

 *i = 10;

 *j = 100.123;

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

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