Программа 28. Выделение и освобождение памяти
В данной программе демонстрируется выделение и освобождение памяти. Для наблюдения за адресами использован оператор вывода <<, который по умолчанию выводит адреса как целые десятичные числа.
// Файл AllocMem.cpp
#include <iostream.h>
#include <math.h>
int main()
{
double *pd; // Указатель
// Вывод адреса, хранящегося в pd
cout << hex << "\n Address(pd) = " << pd;
pd = new double; // Выделение памяти
*pd = sqrt(3); // Занесение в память значения
// Вывод адреса и значения
cout << "\n Address(pd) = " << pd << ", Value(*pd) = " << *pd;
char *s; // Символьный указатель
cout << "\n Address(s) = " << (int*)s;
delete pd; // Освобождение памяти
s = new char[80]; // Создание динамического массива
cout << "\n Address(s) = " << (int*)s << ", Value(s) = " << s;
cout << "\n Input a string \n"; // Введите строку
cin.getline(s, 80); // Использование памяти
cout << "Address(s) = " << (int*)s << ", Value(s) = " << s;
cin.get();
return 0;
}
Программа выводит следующее:
Address(pd) = 1
Address(pd) = 8f5fc0, Value(*pd) = 1.73205
Address(s) = 100
Address(s) = 8f5fc0, Value(s) = ∟vg2∟vg2
Input a string
qwerty
Address(s) = 8f5fc0, Value(s) = qwerty
В программе в выходной поток направляется манипулятор hex, устанавливающий режим вывода целых в шестнадцатеричной форме, благодаря чему адреса выводятся как шестнадцатеричные числа.
Оператор << выводит значение адреса при выводе указателя, за исключением указателей типа char*. При выводе таких указателей печатается не значение указателя (адрес), а строка символов, расположенная по адресу, хранящемуся в указателе, поэтому, чтобы напечатать значения указателя типа char*, производится его приведение к типу int* выражением (int*)s.
Из полученных результатов видно, что указатели pd и s после своего создания имеют значение случайного адреса памяти, обращаться по которому небезопасно, так как он может принадлежать какими-то программам, например, операционной системе.
Массив s создается, после того, как память от pd освобождена. Видно, что под s выделяется то же место в памяти, которое раньше занимало число double, адрес которого хранился в pd.
Задачи 125-134. Указатели и ссылки
Указатель есть адрес некоторого объекта. Если p – указатель на объект, то *p есть сам объект. Адрес объекта возвращает оператор &, который ставится перед именем объекта.
125. Напишите функцию copy(char *s, char *t) копирования строки t в строку s, используя указатели.
126. Перепишите функцию revers (программа 22) с использованием указателей.
127. Используя указатели, напишите функцию strcat(char *s, char *t), копирующую строку t в конец строки s.
128. Напишите функцию strend(char *s, char *t), которая выдает 1, если строка t расположена в конце строки s, и нуль в противном случае.
129. Напишите функцию, которая получает в качестве аргумента указатель на функцию double (*f)(double x) и возвращает минимальное значение функции f на отрезке [a, b].
130. Напишите функцию, которая получает в качестве аргумента указатель на функцию double (*f)(double x) и возвращает среднее значение функции f на отрезке [a, b], вычисленное по n узловым точкам.
131. Напишите функцию, которая получает в качестве аргумента указатель на функцию double (*f)(double x) и возвращает разницу между максимальным и минимальным значение функции f на отрезке [a, b].
132. Напишите функцию, которая возвращает ссылку на максимальный из трех своих аргументов. Используя ее, замените значение максимального из трех чисел их средним значением..
133. Напишите функцию, возвращающую ссылку на максимальный элемент массива. Используйте ее в программе, которая заменит значение максимального элемента массива на среднее арифметическое значение элементов этого массива.
134. Напишите функции, возвращающие ссылки на максимальный и минимальный элементы массива. Используйте их для обмена значениями максимального и минимального элементов.
Глава 11. О файлах и командной
строке
Целью данной главы является, в основном, знакомство с командной строкой программы. Для того, чтобы привести более содержательные примеры, кратко рассмотрена работа с файлами.
Знакомство с файлами
Файлом называется поименованная часть памяти диска, содержащая некоторый набор записей. Непосредственную работу с файлами выполняет операционная система. В языках программирования обычно имеются высокоуровневые средства для доступа к файлам. В C++ они объявлены в заголовочном файле fstream.h.
Дляобозначения источника или приемника данных используется термин поток. Примерами потоков являются стандартные потоки для ввода cin и для вывода cout. Для чтения из файла или записи в файл создаются потоки, связанные с файлами. Существуют два вида потоков: текстовые и бинарные.
Текстовый поток – это последовательность символьных строк, заканчивающихся символом новая строка ’\n’. При выводе перед каждым символом ’\n’ помещается символ возврата каретки ’\r’. При вводе происходит обратное преобразование – два символа ’\r’и ’\n’ заменяются одним символом ’\n’. Необходимость такого преобразования вызвана различиями в фиксации признака новой строки в C++ и в операционной системе. В языке C++ новая строка обозначается единственным символом ’\n’, реально же на диске в файле хранятся оба символа ’\r’и ’\n’, генерируемые нажатием клавиши Enter, которая используется при завершении ввода очередной строки с помощью клавиатуры.
Бинарный поток – это последовательность байтов, которая не подвергается какому-либо преобразованию.
При каждом пуске программы открыты три стандартных потока:
cin – стандартный ввод,
cout – стандартный вывод,
cerr – стандартный приемник ошибок.
По умолчанию стандартный ввод связан с клавиатурой, стандартный вывод – с экраном дисплея. Стандартный ввод, и вывод можно перенаправить на файлы средствами операционной системы, о чем будет говориться ниже. Поток cerr нельзя перенаправить на файл, он всегда связан с экраном (со стандартным утройством вывода).
В файле fstream.h объявлены специальные потоковые типы данных (классы) для работы с файлами. Это классы:
ifstream – для чтения из файла,
ofstream – для записи в файл.
Для работы с файлами в программе нужно определить переменные этих классов, например,
ifstream fin; // Входной файловый поток
ofstream fout; // Выходной файловый поток
Файловые потоки связываются с конкретными файлами функцией:
open(char* filename),
аргументом которой является строка с именем файла, например,
fin.open(”InpData.txt”); // InpData.txt файл для чтения
fout.open(”ResData.txt”); // ResData.txt файл для записи
Для проверки успешности завершения функции open к потоку можно применить оператор !, который возвращает ненулевое значение при наличии ошибки. Например, проверить, что файл InpData.txt удалось открыть, можно следующим образом:
if(!fin != 0){
cerr << ”Не могу открыть файл” << ”InpData.txt”;
exit(1);
}
По умолчанию потоки открываются на чтение и запись как текстовые. Работа с бинарными потоками будет рассмотрена позже.
Для файловых потоков применимы все рассмотренные ранее операторы и функции ввода и вывода.
Открытые файловые потоки закрываются автоматически при завершении программы или при выходе из функции, в которой потоки были созданы. Поток можно явно закрыть, вызвав для него функцию close().