Динамическое управление памятью (new, delete).
Все глобальные переменные и константы, объявленные в программе на языке С++, размещаются в одной непрерывной области оперативной памяти, которая называется сегментом данных. Длина сегмента данных определяется архитектурой процессора 8086 и составляет 64 Кбайта, что может вызвать определенные затруднения при обработке больших массивов данных. С другой стороны, объем памяти ПЭВМ достаточен для успешного решения задач с большой размерностью данных. Выходом из положения может служить использование так называемой динамической памяти.
Динамическая память – это оперативная память ПЭВМ, представляемая программе при её работе, за вычетом сегмента данных, стека, и собственно тела программы. Размер динамической памяти может варьировать в широких пределах. Динамическая память – это фактически единственная возможность обработки массивов данных большого размера. Существуют и другие задачи, которые трудно или невозможно решить без использования динамической памяти. В частности, такие проблемы возникают при разработке систем автоматизированного проектирования: размерность математических моделей может значительно отличаться в различных проектах, статическое распределение памяти в этом случае практически невозможно. Кроме того, динамическая память используется для временного запоминания данных при работе с графическими и звуковыми средствами ПЭВМ.
Динамическое размещение данных означает использование динамической памяти непосредственно при работе программы. Статическое размещение осуществляется компилятором в процессе компиляции программы. При динамическом размещении заранее не известны ни тип, ни количество размещаемых данных, к ним нельзя обращаться по именам, как статическим переменным.
Для управления динамической памятью используются уже знакомые нам указатели. Вся динамическая память рассматривается как подобная стеку структура, называемая кучей.
Для выделения памяти под любую переменную используется операция new [2]:
указатель = new имя_типа инициализатор
Эта операция позволяет выделить и сделать доступным свободный участок в основной памяти, размеры которого соответствуют типу данных, определяемому именем типа. В выделенный участок памяти заносится значение, определяемое инициализатором, который не является обязательным элементом. В случае успешного выполнения операция new возвращает адрес начала выделенного участка памяти. Если участок нужных размеров не может быть выделен (нет памяти), то операция new возвращает нулевое значение указателя (NULL). Необязательный параметр инициализатор – это выражение в круглых скобках. Указатель должен ссылаться на тот же тип, что имя_типа в операции new.
В дальнейшем доступ к выделенному участку памяти осуществляется с помощью операции косвенной адресации (или разыменовывание). Продолжительность существования выделенного участка – от точки создания до конца программы или до явного освобождения. С помощью операции
Delete указатель
осуществляется явное освобождение памяти.
Если указатель, на который действует операция delete, не содержит адрес блока, зарезервированного ранее операцией new, то последствия будут непредсказуемыми.
23.Динамические массивы в языке программирования С++.
к элементам одномерного массива можно обращаться по индексам и по указателю с помощью косвенной адресации, а к элементам двумерного массива - только по указателю. Чтобы работать с индексами, необходимо вспомнить, что двумерный массив это массив одномерных массивов. Имя одномерного массива является указателем на первый элемент массива. Таким образом, двумерный массив можно рассматривать как одномерный массив указателей на первые элементы строк матрицы. При этом усложняется выделение и освобождение динамической памяти.
24.Использование указателей при обработке массивов в языке программирования С++.
Известно, что данные хранятся в ячейках памяти компьютера. Все ячейки памяти пронумерованы. Номера ячеек памяти называются адресами. Указатели используются для работы с адресами. Указатель – это некоторое си мволическое представление адреса. Это означает, что будем работать с переменными, хранящими эти адреса. Описываются такие переменные следующим образом:
<тип> *<идентификатор>;
Такое описание синтаксически отличается от описания простой переменной только наличием знака * перед именем переменной. Как видно из описания, указатель всегда связывается с переменной какого-то определенного типа. Это позволяет определить, сколько байт памяти необходимо выделить по указанному адресу. В переменной типа указатель хранится адрес первого байта, выделенного участка памяти.
Над указателями можно выполнять следующие операции:
· Одному указателю можно присвоить значение другого указателя, если они ссылаются на один и тот же тип
· Значение указателя можно увеличить или уменьшить на константную величину
· Указателю можно присвоить значение адреса. Для получения адреса используется знакомый значок &
· Можно использовать операцию косвенной адресации. Эта операция обозначается значком * и позволяет получить доступ к значению перемен-ной, на которую ссылается указатель
· Можно получить разность двух указателей. Это используется чаще всего для указателей, ссылающихся на один и тот же массив.
Здесь следует обратить внимание на то, что символы * и & имеют разное назначение в зависимости от контекста, в котором они используются.
Очень часто в С++ при работе с массивами вместо индексов использу-ются указатели. Преимущество использования указателей в массивах состоит в том, что арифметические операции над указателями выполняются быстрее, если мы работаем с подряд идущими элементами массива. Если выбор элементов случайный, то быстрее и нагляднее работа с индексами. Имя массива является указателем на первый элемент массива. Элементы массива располагаются в непрерывной области памяти. Эти два положения позволяют перемещаться по элементам массива, используя указатель.
25.Описание функций, фактические и фоpмальные паpаметpы в языке программирования С++.
Программа в языке С++ состоит из функций. Функции – относительно самостоятельные фрагменты программы, спроектированные для решения конкретных задач и снабженные именем . С одной из них, главной, уже знакомы. Функции аналогичны программам в миниатюре и имеют общее название подпрограммы. Главное отличие простой функции от главной в том, что с главной функции начинается выполнение программы, а все остальные функции вызываются либо главной, либо из других функций. Применение функций (подпрограмм) дает возможность сэкономить память за счет размещения многократно повторяющихся частей программ в функции, а также возможность конструировать программу как набор отдельных подпрограмм, что, в свою очередь, позволяет работать группе программистов над сложной задачей. Даже в том случае, если некоторая задача решается в одной функции и одним программистом, лучше оформить ее решение в виде подпрограмм, поскольку функции повышают уровень модульности, облегчают чтение, внесение изменений и коррекцию ошибок в программе.
В программе на языке С++ описание функций может располагаться либо до функции main, либо после функции main. Никакая функция не может быть описана внутри другой функции. Кроме того, функции могут быть описаны в отдельном файле и подключены к программе с помощью директивы препроцессора include. Каждая функция описывается один раз, а вызываться на выполнение может многократно. В языке C++ описание должно обязательно предшествовать вызову функции. Если функция описана после функции, из которой она вызывается, то до вызова функции должен быть помещен прототип функции. Прототип функции – это заголовок функции, завершающийся точкой с запятой (функция должна быть декларирована).
Структура подпрограммы почти полностью повторяет структуру главной функции, что как раз подчеркивает структурированность языка. В функциях могут быть описаны собственные константы, типы, переменные. В этом случае они называются локальными. Их область действия распространяется только на те функции, в которых они описаны. Переменные, описанные вне функций, в том числе и вне главной функции, называются глобальными. Их область действия распространяется на все функции, расположенные в том же файле после описания. Имена локализованных переменных могут совпадать с ранее объявленными глобальными именами. В этом случае считается, что локальное имя "закрывает" глобальное и делает его недоступным, т. е. одно-именные локальные и глобальные переменные ? это разные переменные. Память под глобальные переменные отводится до начала выполнения программы и освобождается в момент завершения программы. Доступ к ним возможен из любой функции. Память под локальные переменные выделяется в момент вызова подпрограммы и освобождается после завершения её выполнения. Доступ к ним возможен только из той подпрограммы, в которой они описаны.
В общем случае структуру подпрограммы можно представить следующим образом:
Заголовок функции
Тело функции
Общая форма записи заголовка функции:
<тип> <имя> ([<список формальных параметров>])
Здесь <имя> - правильный идентификатор, который используется для обращения к функции;
<тип> - тип возвращаемого функцией результата;
<список формальных параметров> - включает в себя параметры, необходимые для нормального интерфейса, т. е. позволяет передавать данные в подпрограмму и из подпрограммы. Список формальных параметров необязателен и может отсутствовать (круглые скобки опускать нельзя). Если он есть, то может включать в себя параметры, передаваемые по значению, указателю и ссылке.
Таким образом, в списке формальных параметров перечисляются имена формальных параметров и их тип. Список формальных параметров расширяет раздел описаний функции, и формальные параметры наряду с локальными переменными используются в выражениях. Тело функции, в том числе и функции main, заключается в фигурные скобки и содержит описания и операторы.
Вызов функции производится при помощи оператора вызова функции:
<имя >(<список фактических параметров>);
Он записывается в том месте программы, где по логике решения задачи требуется выполнение операторов, определенных в теле функции. После выполнения операторов тела функции управление передается оператору, следующему за оператором вызова.
Следует заметить, что таким образом вызываются функции типа void, т. е. функции, не возвращающие никакого значения. Функции же, возвращающие значения типа int, float и т. д., вызываются упоминанием имени в выражении аналогично стандартным функциям sin, cos и другим. Поэтому в теле таких функций обязательно должен присутствовать оператор возврата return. После ключевого слова return записывается выражение, значение которого вставится вместо имени функции в точке вызова. Функция не может возвращать массив или функцию. В качестве результата, возвращаемого функцией, могут быть значения только простого и ссылочного типа. Оператор return приводит к немедленному выходу из функции. Если функция имеет тип void, то оператор return может быть использован без возвращаемого значения.
К списку параметров предъявляются следующие требования:
· количество формальных параметров должно быть равно количеству фактических параметров;
· порядок следования фактических и формальных параметров должен быть один и тот же;
· тип каждого фактического должен совпадать с типом соответствующего формального параметра.
фактический параметр — аргумент, передаваемый в функцию при ее вызове;
формальный параметр — аргумент, указываемый при объявлении или определении функции.
26.Передача параметров по значению, по ссылке, по указателю в языке программирования С++.
По значению.
Передача параметра по значению означает что вызывающая функция копирует в память, доступную вызываемой, (обычно стек) непосредственное значение. Изменение копии переменной, соответственно, оригинал не затрагивает.
По адресу.
Если необходимо именно изменить переменную из внешней, по отношению к вызываемой функции, области видимости, можно копировать адрес переменной, подлежащей изменению. Соответственно при вызове функции g(&x) приходится использовать операцию взятия адреса. Эта техническая деталь отвлекает внимание программиста от логики прикладной программы, однако в случаях невозможности передачи по ссылке может оказаться единственным решением.
Можно заметить, что передача параметра по адресу является частным случаем передачи по значению: передаваемым значением является адрес, по которому можно найти другое значение — значение переменной x.
По ссылке.
Передача параметра по ссылке означает что копируется не само значение, а адрес исходной переменной (как в случае передачи параметра по адресу), однако синтаксис используется такой, чтобы программисту не приходилось использовать операцию разыменования и он мог иметь дело непосредственно со значением, хранящимся по этому адресу (как в случае передачи параметра по значению).
Передача по ссылке позволяет избежать копирования всей информации, описывающей состояние объекта (а это может быть существенно больше чем sizeof(int)) и является необходимой для конструктора копирования.
Если функция возвращает значение по ссылке (например, в виде «return *this;»), то её вызов можно использовать слева от оператора присваивания.
В случае если передача по ссылке используется именно как средство увеличения быстродействия, но изменение параметра нежелательно, можно использовать передачу по ссылке константного объекта.
Таким образом можно ожидать, что примерная программа напечатает (если закоментировать ошибочную строку) «0010 022 233 333».
27.Строки символов в языке программирования С++.
Символьная переменная – это величина размером в 1 байт, которая используется для представления литер и целых чисел в диапазоне от 0 до 255 или от –128 до 127. Диапазон зависит от того, знаковая это переменная или нет. Символьные константы заключаются в апостро фы. Например, “a”, ”=”, “9”. Используется специальный код для перевода чисел в символы. Чаще всего это код ASCII (американский стандартный код обмена информацией). Латинская буква А имеет код 65, а русская буква А – 128. Поэтому при работе с русским алфа витом, необходимо использовать беззнаковые целые (unsigned char), диапазон представления которых лежит в интервале от 0 до 255. Буквы латинского алфавита в таблице ASCII расположены по порядку. Буквы русского алфавита имеют разрыв, но их код возрастает в соответствии с алфавитом. Некоторые символы не печатаются, в этом случае используется номер символа с обратной косой чертой, например “\007” (этот символ используется для выдачи короткого звукового сигнала) или “\n” (перевод курсора на новую строку).
Для ввода символов можно использовать специальные функции ввода-вывода. Функция getchar () получает один символ, поступающий с клавиатуры, и передает его выполняющейся в данный момент программе. Функция putchar() получает один символ, поступающий из программы, и передает его для вывода на экран.
Здесь следует отметить, что при подсчете символов будут учитываться все символы, включая пробелы и перевод курсора на новую строку “\n” (при нажатии клавиши enter). Кроме того, здесь впервые используется потоковый ввод-вывод. Для того чтобы воспользоваться потоковым вводом- выводом, в текст программы включена директива препроцессора
#include <iostream.h>
Символьные строки представляют один из наиболее полезных и важных типов данных. В языке С++ символьная строка является символьным массивом, который заканчивается нуль-символом (\0). Если строка должна содержать N символов, то в описании нужно указать N+1 элемент.
Функция gets() получает строку с клавиатуры. Функция читает символы до тех пор, пока не встретит символ новой строки ( “\n”), который создается при нажатии клавиши enter. Функция берет все символы до “\n” и присоединяет к ним символ “\0”. Аргументом функции должен быть указатель на строку символов. Функция puts() выводит строку, указатель на которую является аргументом этой функции, на экран. Функция puts() прекращает свою работу, как только встретит нуль-символ (поэтому надо следить, чтобы он был), и заменяет его символом новая строка.
Для работы со строками существует специальная библиотека, описание которой находится в файле string.h. Рассмотрим наиболее часто используемые функции из этой библиотеки.
· strlen (s) - определяет длину строки (без нуль-символа). s – указатель на строку символов.
· strcat (s1, s2) – объединяет две с троки. s1, s2 – указатели на строку символов. Копия второй строки присоединяется к концу первой, и это объединение становится новой первой строкой. Строка s2 остается без изменения, а s1должна быть достаточно большой, чтобы разместить две строки.
· strcmp (s1, s2) – сравнивает содержимое двух строк. s1, s2- указатели на строку символов. Эта функция возвращает 0, если строки одинаковые. Если строки разные, то функция возвращает разницу в кодах у первой пары несовпадающих символов.
· strcpy (s1, s2) – копирова ние строки: строка, на которую указывает второй аргумент, копируется в строку, на которую указывает первый.
· strchr (s, c) – ищет в строке s первое вхождение символа с и возвращает указатель на этот символ, если не обнаружит, то возвращает NULL.
· strstr (s1, s2) – ищет в строке s1 первое вхождение подстроки s2 и возвращает указатель на найденную подстроку, если не обнаружит, то возвращает NULL.
Для того чтобы встретился конец файла при вводе с клавиатуры, необходимо нажать одновременно клавиши CTRL и Z.
Для записи строки в файл используется функция fputs, аналогичная puts, но имеющая в качестве второго параметра указатель на файл, в который намерены писать строку символов. Функция fgets, аналогичная gets, служит для чтения из файла строки символов и имеет следующий синтаксис:
char *fgets(char *s, int n, FILE * stream)
Эта функция считывает из файла с указателем stream в строку s символы до тех пор, пока не будет выполнено одно из условий:
· начнется новая строка;
· достигнут конец файла;
· прочитано n-1 символов
fgets (s, 20, in);// Читается не более 20 символов
После того как из файла в строку s будут прочитаны символы, строка дополняется символом нуль. Если при чтении встретился символ конца строки, то он переносится в строку s и нулевой символ записывается за ним. Если операция считывания прошла успешно, то возвращается адрес строки s, в противном случае возвращается значение NULL. Т. е. значение NULL возвращается, если встретится символ конец файла.
28.Стpуктуpы и объединения в языке программирования С++.
Структуры это тип данных, состоящий из фиксированного числа компонентов, называемых полями. В отличие от массива компоненты (поля) структуры могут быть различного типа. Очень часто возникает необходимость описать характеристики некоторого объекта, представляемого и обрабатываемого в программе. Таким объектом могут быть данные о человеке, автомобиле, журнале и т. д.
Объявление структуры:
struct < имя типа >{
< сп. полей >
};
Здесь struct ключевое слово; < имя типа > правильный идентификатор; < сп. полей > список полей, представляющий собой последовательность разделов, между которыми ставится точка с запятой.
Как элементы массива, так и поля структуры можно использовать в ка-честве отдельных переменных. Чтобы можно было ссылаться на то или иное поле структуры, поля именуются. К каждому полю можно обратиться, если указать имя переменной типа struct, затем точку и имя поля.
Для структур одного типа можно проверить выполнение отношения равенства или неравенства. Как и в случае массивов, допустимы операции сравнения = = и !=.
Структуры могут быть элементами массива.
Поле структуры само может быть структурой.
Со структурами в близком родстве находятся объединения, которые описываются с помощью ключевого слова union . Синтаксис описания объединения отличается от описания структур только ключевым словом:
union < имя типа >{
< сп. полей >
};
Здесь < имя типа > правильный идентификатор; < сп. полей > список полей, представляющий собой последовательность разделов, между которыми ставится точка с запятой.
Объединения позволяют запоми-нать данные различных типов в одном и том же месте памяти. В каждый момент времени запоминается только одно значение. Нельзя записать два значения одновременно, даже если для этого достаточно памяти.
29.Битовые поля в языке программирования С++.
В отличие от других языков программирования язык Си обеспечивает доступ к одному или нескольким битам в байте или слове. Внутри структур и объединений могут в качестве их компонентов использоваться битовые поля. Каждое битовое поле представляет целое или беззнаковое целое значение, занимающее в памяти фиксированное число битов (от 1 до 16). Битовые поля могут быть только элементами структур, объединений и классов, т. е. они не могут быть самостоятельными объектами программ. Битовые поля не имеют адресов, нет указателей и ссылок на битовые поля. Они не могут объединять-ся в массивы. Назначение битовых полей обеспечить удобный доступ к от-дельным битам данных. Чаще всего битовые поля используют при работе с флажками, которые могут принимать только два значения, что позволяет экономить память.
Синтаксис описания структуры с битовыми полями следующий:
struct <имя>{
<тип поля> имя поля1: длина в битах;
<тип поля> имяполя2: длина в битах;
<тип поля> имяполяN: длина в битах;
}
Имя поля может быть пропущено, тогда соответствующее количество байтов не используется (пропускается). Обычно эта возможность использу-ется для изменения порядка размещения битовых полей, выравнивая их по границам слов.
30.Двоичные файлы в языке программирования С++.
Двоичные файлы это последовательность значений типа char. Использование двоичных файлов делает программы немобильными при переносе из одной среды в другую. Любой набор данных может быть представлен как набор символов, но такое представление может изменяться при переходе от одной реализации Си к другой.
Двоичные файлы имеют следующие преимущества по сравнению с текстовыми: возможность прямого доступа к компонентам файла и возможность запи-си/чтения за одно обращение к файлу нескольких (не одного) компонентов файла.
Длина любого компонента двоичного файла строго постоянна, что дает возможность организовать прямой доступ к каждому компоненту.
Открытие двоичного файла производится той же функцией, как в тек-стовом файле, но тип открываемого файла явно указывается:
in=fopen("input.dat","rb");
Для организации прямого доступа к компонентам двоичного файла слу-жит встроенная функция fseek: