Урок #9

Работа с ссылками и указателями

На этом уроке пойдет речь о довольно интересной и одновременно сложной теме. Тема небольшая, но требует полного ее понимания.

Мы уже умеем работать с обычными переменными и даже не задумываемся о том как они работают. На первый взгляд может показаться, что это сложно, но это не так. Давайте поочередно рассмотрим процесс объявления переменной и присвоения ей значения: short int number = 1. В данном случае создается переменная number типа short int. Но что означает само имя этой переменной? Оно представляет собой некую ссылку на ячейку памяти. То есть указывает на ячейку с определенным номером. Тип переменной определяет размер памяти, которая будет выделяться для нее самой. Например наша переменная number имеет двухбайтный тип short int и это значит, что в памяти будет выделено ровно два байта памяти, то есть 16 бит. Далее идет присвоение переменной значения 1: в ячейку памяти, на которую ссылается переменная number записывается число 1.

Вот мы и разобрались с самим механизмом работы с переменными в программе.

Работа с ссылками

Ссылки используются в основном только в двух случаях: при создании копии переменной, или при передаче переменной в функцию и дальнейшего изменения ее значения в функции. Для того, что бы создать переменную-копию, необходимо перед её именем написать знак & при её объявлении:

256
Для продолжения нажмите любую клавишу…

Важно то, что при объявлении такой копии обязательно сразу писать какую переменную она будет копировать, иначе код не скомпилируется.

Использование ссылок в функциях

Поставим перед собой простую задачу: увеличить значение переменной с использованием функции. Ответ очевидный: создаем функцию mult10 и в ее теле изменяем значение переменной. Казалось бы, что это все. Но это не так. В этом случае переменная изменится лишь в функции, а в main останется прежней. Решением данной задачи будет следующая программа:

160
Для продолжения нажмите любую клавишу…

Эта программа работает отлично, переменная изменяет свое значение, но вам не кажется, что строка number = mult10(number) слишком длинная? Лично мне кажется и сейчас я покажу как ее можно уменьшить до элегантного mult10(number). Это делается довольно легко и ответ напрашивается сам после изучения первой части этого урока: сделать n в функции копией. Тогда функция, написанная с копией будет выглядеть так:

160
Для продолжения нажмите любую клавишу…

Как видите, мы убили сразу двух зайцев: убрали return в функции и сократили строчку с изменением переменной.

Указатели

В этом уроке мы довольно много говорили о том, что переменная указывает на определенную ячейку памяти. Но какой адрес именно имеет данная ячейка? Для этого воспользуемся указателями. Указатели — переменные, которые содержат адрес, в то время как обычная переменная содержит то, что записано в этой ячейке памяти. Убедимся в этом, рассмотрев следующий пример:

256 00AFFEDC
256 00AFFEDC
Для продолжения нажмите любую клавишу . . .

Если вы запустите программу, ваш результат будет отличаться от того, который есть на сайте, потому что программа будет искать подходящую пустую область памяти, а она почти никогда не может быть такой же, как и на другом компьютере. Даже на вашем компьютере при многократном запуске этой программы адреса всегда будут другие, потому что в разное время в памяти свободны разные ячейки. Но эти два адреса будут всегда одинаковыми, потому что переменная b является ссылкой на ячейку, в которой содержится значение переменной a. Обратите внимание на то как мы создаем переменную-указатель. Мы ей присваиваем переменную, на которую хотим ссылаться, только перед ее именем пишем знак &. Он так же используется как оператор взятия адреса.

Арифметика указателей

Удивительно, но указатели можно складывать и вычитать. Но не между собой, а с любым целочисленным типом. Вы можете спросить: «А зачем это?». Ответ прост: так работают массивы. И вправду, имя массива служит указателем на нулевой элемент, а для доступа к другим его элементам надо пользоваться арифметикой указателей. Например, у нас есть массив arr, состоящий из 5-ти элементов и инициализированный так: int arr[] { 1, 2, 4, 8, 16 }. Тогда при выводе лишь имени массива мы выведем адрес первого элемента: cout << arr.

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

Как было сказано ранее, массивы полностью работают на ссылках, а название массива - указатель на его нулевой элемент. Что бы получить значение этого нулевого элемента, воспользуемся оператором *. cout << *arr выведет число 1. Что бы получить доступ к другим элементам массива, будем прибавлять порядковый номер нужного нам элемента массива, а затем их разыменовать. То есть для доступа к пятому элементу массива необходимо написать *(arr + 4). Конечно, можно воспользоваться и обычным способом с квадратными скобками, но с указателями есть несколько интересных моментов, на которые мы сейчас и обратим внимание.

Предлагаю вам вывести адреса первых трех элементов массива. Это можно сделать при помощи такой команды: cout << arr << endl << arr + 1 << endl << arr + 2 <<< endl. Вы можете подумать, что эти значения должны отличаться лишь на единицу. Но нет, они отличаются аж на 4. Почему это произошло именно так? Дело в том, что int является четырехбайтным типом, поэтому для его хранения в памяти нужно целых 4 бита. Вполне логично, что прибавляя номер элемента массива, мы переходим на 4 бита, а не на 1, ибо если перейти лишь на 1 бит, мы получим какой-то мусор, вместо нужного нам результата.

Возможные вопросы по теме:

Вопросов еще нет

Добавляйте свои вопросы в комментарии. Лучшие попадут в статью 🙂


Урок #8 Урок #10