Указатели. Оператор адреса &

Содержание

Слайд 3

Оператор адреса &

Позволяет узнать, какой адрес памяти присвоен определённой переменной. Всё довольно

Оператор адреса & Позволяет узнать, какой адрес памяти присвоен определённой переменной. Всё
просто:
int a = 7;
std::cout << a << '\n'; // выводим значение переменной a
std::cout << &a << '\n'; // выводим адрес памяти переменной a
Результат:
7
0046FCF0

Слайд 4

Оператор разыменования (*)

позволяет получить значение по указанному адресу:
int a = 7;
std::cout

Оператор разыменования (*) позволяет получить значение по указанному адресу: int a =
<< a << '\n'; // выводим значение переменной a
std::cout << &a << '\n'; // выводим адрес переменной a
std::cout << *&a << '\n'; /// выводим значение ячейки памяти переменной a
7
0046FCF0
7

Слайд 5

Указатели

Переменная, значением которой является адрес (ячейка) памяти. Указатели объявляются точно так же,

Указатели Переменная, значением которой является адрес (ячейка) памяти. Указатели объявляются точно так
как и обычные переменные, только со звёздочкой между типом данных и идентификатором:
int *iPtr; // указатель на значение типа int
double *dPtr; // указатель на значение типа double
int* iPtr3; // корректный синтаксис (допустимый, но нежелателен)
int * iPtr4; // корректный синтаксис (не делайте так)
int *iPtr5, *iPtr6; // объявляем два указателя для переменных типа int

Слайд 6

Присваивание значений указателю

Поскольку указатели содержат только адреса, то при присваивании указателю значения

Присваивание значений указателю Поскольку указатели содержат только адреса, то при присваивании указателю
— это значение должно быть адресом. Для получения адреса переменной используется оператор адреса:
int value = 5;
int *ptr = &value; // инициализируем ptr адресом значения переменной

Слайд 7

#include
int main()
{
int value = 5;
int *ptr = &value; //

#include int main() { int value = 5; int *ptr = &value;
инициализируем ptr адресом значения переменной
std::cout << &value << '\n'; // выводим адрес значения переменной value
std::cout << ptr << '\n'; // выводим адрес, который хранит ptr
return 0;
}
Результат:
003AFCD4
003AFCD4

Слайд 8

Тип указателя должен соответствовать типу переменной, на которую он указывает
int iValue =

Тип указателя должен соответствовать типу переменной, на которую он указывает int iValue
7;
double dValue = 9.0;
int *iPtr = &iValue; // ок
double *dPtr = &dValue; // ок
iPtr = &dValue; // неправильно: указатель типа int не может указывать на адрес переменной типа double
dPtr = &iValue; // неправильно: указатель типа double не может указывать на адрес переменной типа int

Слайд 9

Следующее не является допустимым:
int *ptr = 7;
C++ также не позволит вам напрямую

Следующее не является допустимым: int *ptr = 7; C++ также не позволит
присваивать адреса памяти указателю:
double *dPtr = 0x0012FF7C; // не ок: рассматривается как присваивание целочисленного литерала

Слайд 10

Разыменование указателей

 Разыменованный указатель — это содержимое ячейки памяти, на которую он указывает
int

Разыменование указателей Разыменованный указатель — это содержимое ячейки памяти, на которую он
value = 5;
std::cout << &value << std::endl; // выводим адрес value
std::cout << value << std::endl; // выводим содержимое value
int *ptr = &value; // ptr указывает на value
std::cout << ptr << std::endl; // выводим адрес, который хранится в ptr, т.е. &value
std::cout << *ptr << std::endl; // разыменовываем ptr (получаем значение на которое указывает ptr)
Результат:
0034FD90
5
0034FD90
5

Слайд 11

Указатели и массивы

int array[4] = { 5, 8, 6, 4 };

Указатели и массивы int array[4] = { 5, 8, 6, 4 };
//фиксированный массив
Для компилятора array является переменной типа int[4]. Переменная array содержит адрес первого элемента массива, как если бы это был указатель!
int array[4] = { 5, 8, 6, 4 };
// Выводим значение массива (переменной array)
std::cout << "The array has address: " << array << '\n';
// Выводим адрес элемента массива
std::cout << "Element 0 has address: " << &array[0] << '\n';

Слайд 12

Адресная арифметика

Если ptr указывает на целое число, то ptr + 1

Адресная арифметика Если ptr указывает на целое число, то ptr + 1
является адресом следующего целочисленного значения в памяти после ptr. ptr - 1 — это адрес предыдущего целочисленного значения (перед ptr).

Слайд 13

При вычислении результата выражения адресной арифметики (или ещё «арифметики с указателями») компилятор

При вычислении результата выражения адресной арифметики (или ещё «арифметики с указателями») компилятор
всегда умножает целочисленный операнд на размер объекта, на который указывает указатель. Например:
int value = 8;
int *ptr = &value;
std::cout << ptr << '\n';
std::cout << ptr+1 << '\n';
std::cout << ptr+2 << '\n';
std::cout << ptr+3 << '\n';
Результат:
002CF9A4
002CF9A8
002CF9AC
002CF9B0

Слайд 14

Индексация массивов

     int array [5] = { 7, 8, 2, 4,

Индексация массивов int array [5] = { 7, 8, 2, 4, 5
5 };
     std::cout << &array[1] << '\n'; // выведется адрес памяти элемента под номером 1
     std::cout << array+1 << '\n'; // выведется адрес памяти указателя на массив + 1
     std::cout << array[1] << '\n'; // выведется 8
     std::cout << *(array+1) << '\n'; // выведется 8 (обратите внимание на скобки, они здесь обязательны)
array[n] — это то же самое, что и *(array + n), где n является целочисленным значением.

Слайд 15

Память

Статическое
 Память выделяется один раз, при запуске программы, и сохраняется на протяжении работы

Память Статическое Память выделяется один раз, при запуске программы, и сохраняется на
всей программы
Автоматическое
Память выделяется при входе в блок, в котором находятся эти переменные, и удаляется при выходе из него.
Динамическое

Слайд 16

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

Это способ запроса памяти из операционной системы запущенными программами по

Динамическое выделение памяти Это способ запроса памяти из операционной системы запущенными программами
надобности. Эта память не выделяется из ограниченной памяти стека программы, а из гораздо большего хранилища, управляемого операционной системой — кучи.
new int; // динамически выделяем целочисленную переменную и сразу же отбрасываем результат (так как нигде его не сохраняем)
int *ptr = new int; // динамически выделяем целочисленную переменную и присваиваем её адрес ptr, чтобы потом иметь доступ к ней
Затем мы можем разыменовать указатель для получения значения:
*ptr = 8; // присваиваем значение 8 только что выделенной памяти

Слайд 17

Когда уже всё, что нужно было, выполнено с динамически выделенной переменной —

Когда уже всё, что нужно было, выполнено с динамически выделенной переменной —
нужно явно указать С++ освободить эту память. Для переменных это выполняется с помощью оператора delete:
// Предположим, что ptr ранее уже был выделен с помощью оператора new
delete ptr; // возвращаем память, на которую указывал ptr, обратно в операционную систему
ptr = 0; // делаем ptr нулевым указателем // Предположим, что ptr ранее уже был выделен с помощью оператора new
Без удаления произойдет утечка памяти!

Слайд 18

int *value = new (std::nothrow) int; // запрос на выделение динамической памяти

int *value = new (std::nothrow) int; // запрос на выделение динамической памяти
для целочисленного значения
if (!value) // обрабатываем случай, когда new возвращает null (т.е. память не выделяется)
{
// Обработка этого случая
std::cout << "Could not allocate memory";
}

Слайд 19

Динамические массивы

std::cout << "Enter a positive integer: ";
int length;

Динамические массивы std::cout int length; std::cin >> length; int *array = new
std::cin >> length;
int *array = new int[length]; // используем оператор new[] для выделения массива.
std::cout << "I just allocated an array of integers of length " << length << '\n';
delete[] array; // используем оператор delete[] для освобождения выделенной для массива памяти
array = 0;

Слайд 20

Инициализация

int fixedArray[5] = { 9, 7, 5, 3, 1 }; //

Инициализация int fixedArray[5] = { 9, 7, 5, 3, 1 }; //
инициализируем фиксированный массив
int *array = new int[5] { 9, 7, 5, 3, 1 }; // инициализируем динамический массив