4390

Указатели и ссылки в языке С++

Контрольная

Информатика, кибернетика и программирование

Указатели и ссылки в языке С++ Указатели Обычно программисту не нужно знать реальный адрес каждой переменной, поскольку компилятор способен сам позаботиться о таких подробностях. Но если необходимость в этой информации все же возникает, то пол...

Русский

2012-11-18

57.5 KB

15 чел.

Указатели и ссылки в языке С++

  1.  Указатели

Обычно программисту не нужно знать реальный адрес каждой переменной, поскольку компилятор способен сам позаботиться о таких подробностях. Но если необходимость в этой информации все же возникает, то получить ее можно с помощью оператора обращения к адресу: &. Например, если в программе используется переменная Var типа unsigned short, то набрав команду:

cout<<&Var;

мы получим результат: 0012FF7C – адрес этой переменной в шестнадцатеричном коде. На другом компьютере результат может получиться другим.

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

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

Предположим, что существует целочисленная переменная howOld. Чтобы объявить указатель на нее по имени pAge, применяется следующая форма записи:

int *pAge=0;

В результате будет получена переменная pAge, предназначенная для хранения адреса значения типа int. В этом примере переменная pAge инициализируется нулевым значением. Указатели, значения которых равны нулю, называются пустыми (null pointer). После объявления указателю обязательно нужно присвоить какое-либо значение. Если предназначенный для хранения в указателе адрес заранее не известен, ему следует присвоить нулевое значение. Неинициализированные указатели называются дикими (wild pointer). Они очень опасны. Не шутите с этим, всегда инициализируйте указатели!

Присвоить адрес переменной howOld указателю pAge можно следующим образом:

int howOld=50;

int *pAge=0;

pAge=&howOld;

 Две последние строки можно объединить в одну:

int howOld=50;

int *pAge=&howOld;

В указателях звездочка (*) используется двумя различными способами: при объявлении указателя и в операторе косвенного доступа. При объявлении указателя звездочка (*) является частью синтаксиса и располагается после указания типа объекта. Например:

// объявить указатель на тип int

int *pAge = 0;

При косвенном доступе звездочка означает, что речь идет о значении в памяти, находящемся по адресу в данной переменной, а не о самом адресе.

// разместить значение 5 по адресу, находящемуся в pAge

*pAge = 5;

Заметьте, что тот же символ (*) используется как оператор умножения. Что именно имел в виду программист, поставив звездочку, компилятор определяет, исходя из контекста.

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

  •  область глобальных переменных;
  •  динамически распределяемая память;
  •  регистры;
  •  сегменты программного кода;
  •  стек.

Стек представляет собой память, организованную по принципу LIFO (“Last In, First Out” – «Последним вошел, первым вышел»). Локальные переменные размещаются в стеке. Код программы размещается в сегментах. Регистры используются для внутренних целей процессора, например, для контроля вершины стека и указателя команд. Остальная часть памяти составляет так называемую динамически распределяемую память, или heap (кучу).

Особенностью локальных переменных является то, что после выхода из функции, в которой они были объявлены, память, выделенная для их хранения, освобождается, а значения переменных уничтожаются. Глобальные переменные позволяют частично решить эту проблему ценой неограниченного доступа к ним из любой точки программы, что значительно усложняет восприятие текста программы. Использование динамической памяти полностью решает обе проблемы.

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

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

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

Для выделения участка в динамически распределяемой области памяти используется ключевой слово new. Например, чтобы создать в динамически распределяемой памяти переменную типа int, необходимо ввести команду:

int *pPointer = new int;

Теперь мы можем записать

  *pPointer=72;

что можно прочитать так: «Разместить число 72 в той области динамически распределяемой памяти, на которую указывает pPointer».

По завершении работы с выделенной областью памяти ее нужно освободить. Для этого применяется оператор delete, после которого следует имя указателя. Например:

delete pPointer;

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

Поэтому каждый раз, когда в программе используется оператор new, за ним должен следовать оператор delete. Наилучшим способом нажить себе неприятностей является переприсвоение указателя без предварительного освобождения участка памяти, на который он указывает. Это приводит к утечке памяти. Рассмотрим следующий фрагмент кода:

int *pPointer = new int;

*pPointer = 72;

pPointer = new int;

*pPointer = 84;

В первой строке объявляется указатель pPointer и выделяется память для хранения переменной типа int. В следующей строке в выделенную область записывается значение 72. Затем в третьей строке указателю присваивается адрес другой области памяти, в которую записывается число 84 (строка 4). Теперь исходный участок памяти, содержащий значение 72, оказывается недоступен, поскольку указателю на эту область было присвоено новое значение. В результате невозможно ни использовать, ни освободить зарезервированную память до завершения программы. Правильно этот фрагмент выглядел бы так:

int *pPointer = new int;

*pPointer = 72;

delete pPointer;

pPointer = new int;

*pPointer = 84;

  1.  Ссылки

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

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

int &rSomeRef=someInt;

Это можно прочитать как «rSomeRef  является ссылкой на целочисленное значение, инициализированное адресом переменной someInt».

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

  1.  Передача аргументов функции по ссылке

Функции имеют два ограничения: аргументы передаются как значения и теряют связь с исходными данными, а возвращать функция может только одно значение. Передавая функции аргументы по ссылке, можно преодолеть оба ограничения. В языке С++ передача данных по ссылке осуществляется двумя способами: либо с помощью указателей, либо с помощью ссылок. Ниже приведены примеры реализации функции swap(), обменивающей значения двух переменных, с помощью указателей и с помощью ссылок.

Листинг 6.1. Реализация функции swap() для работы с указателями

void swap (int *px, int *py)

{

int temp;

temp = *px;

*px = *py;

 *py = temp;

}

Для вызова функции swap() из основной программы используется команда swap(&x, &y).

Листинг 6.2. Реализация функции swap() для работы со ссылками

void swap (int &rx, int &ry)

{

int temp;

temp = rx;

rx = ry;

ry = temp;

}

Для вызова функции swap() из основной программы используется команда swap(x, y).

Как можно видеть, при работе со ссылками функции swap() передаются именно значения x и y, а не их адреса. Таким образом, благодаря использованию ссылок функция приобретает новую возможность изменять исходные данные в вызывающей функции, хотя при этом сам вызов функции ничем не отличается от обычного.

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

  1.  Возвращение нескольких значений

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

Листинг 6.3. Возвращение из функций нескольких значений при помощи ссылок

#include <iostream.h>

enum ERR_CODE {SUCCESS, ERROR};

ERR_CODE Factor(int, int&, int&); //Объявление функции

int main() {

int number, squared, cubed;

ERR_CODE result;

cout << ”Enter a number (0 – 20); “;

cin >> number;

result = Factor (number, squared, cubed);

if (result == SUCCESS)

{

cout << “number: “ << number << endl;

cout << “squared: “ << squared << endl;

cout << “cubed: “ << cubed << endl;

}

else

cout << “Error encountered!!” << endl;

char res;

cin>>res;

}

// Реализация функции

ERR_CODE Factor(int n, int &rSquared, int &rCubed) {

if (n > 20)

return ERROR;

else

{

rSquared = n*n;

rCubed = n*n*n;

return SUCCESS;

}

}

В этой программе функция Factor() вначале объявляется, а затем реализуется. Такая структура типична для профессиональных программ. Объявление записано выше функции main(), а реализация – ниже ее. В объявлении не обязательно писать имена аргументов, нужно только указать их типы. Объявление функции заканчивается точкой с запятой (в отличие от заголовка реализации функции).


 

А также другие работы, которые могут Вас заинтересовать

36896. Спрощена інструкція по роботі з СППР PRIME Decisions 385.5 KB
  для всіх атрибутів які використовуються для прийняття рішень. Введення альтернативних значень для готової цілі та для атрибутів на основі яких здійснюється вибір. Введення альтернатив для головної цілі та для атрибутів в додатковому вікні lterntives 3. Ввести у відповідні стовпці альтернативні значення для атрибутів на основі якого здійснюється вибір.
36897. Адміністрування локальної мережі з використанням FRIENDLY PINGER 1.75 MB
  ІНСТРУКЦІЯ до проведення лабораторної роботи №19 Адміністрування локальної мережі з використанням FRIENDLY PINGER Предмет: Компютерна практика Автор і дата затвердження програми: Новиченко В. Київ Лабораторна робота № 19 Адміністрування локальної мережі з використанням FRIENDLY PINGER 1 Мета роботи Ознайомитись з можливостями програми FRIENDLY PINGER призначеної для адміністрування моніторингу й інвентаризації комп'ютерних мереж. Побудувати топологію локальної мережі згідно з варіантом завдання 2. Сучасні компютерні мережі.
36898. Исследование характеристик усталостной прочности конструкционных материалов при циклических испытаниях 730.5 KB
  Цель работы: Ознакомиться с проблемой усталости авиационных конструкций; параметрами характеризующими цикл нагружения; существующими разновидностями цикла напряжения; характеристиками сопротивления усталости при регулярном напряжении; кривой усталости. ВОПРОСЫ Перечислить основные этапы в гражданской авиации которые связаны с проблемой усталости конструкции самолета. Чем они характеризуются Дать определение...
36899. Ознакомление с работой на учебной микро-ЭВМ 171.73 KB
  Задание 1 Краткое назначение блоков структурной схемы микроЭВМ БП блок микропроцессора и схем обрамления обеспечивающих его работу формирующий МД МА и сигналы управления микроЭВМ. БУ блок управления режимами работы МП. БИСМ блок индикации состояния магистралей. БУКП блок управления картой памяти.
36900. Изучение принципа работы ОЗУ 356.65 KB
  Изучение принципа работы ОЗУ Цель работы: исследование принципа работы ОЗУ и мультиплексного способа организации общей шины. Задание 1 УГО ОЗУ в соответствии с рисунком 1 А0А3 адресные входы; CS выбор микросхемы; W R запись считывание; DIO1DIO8 совмещенные информационные вводывыводы Рисунок 1 Задание 2 Провести очистку ячеек памяти ОЗУ по адресам 0 1 2 3 4 D E F Задание 3 Записать 10 8 по адресам 6 11 считать записанную информацию Задание 4 Составить алгоритм работы ОЗУ Алгоритм работы ОЗУ в соответствии с рисунком 2...
36901. КОНТРОЛЬ РАЗМЕРОВ ЦИЛИНДРИЧЕСКИХ ДЕТАЛЕЙ 353 KB
  Цель работы приобрести первичные практические навыки в выполнении измерений с помощью различных универсальных измерительных средств приобрести навыки в оценке годности детали по линейным размерам I. С помощью выбранных универсальных измерительных средств определить действительные размеры проверяемой детали результаты занести в столбцы 712 таблицы 1 и дать заключение о ее годности. Варианты заданий Номер Контролируемые параметры детали образцов А1 А2 А3 А4 А5 А6 А7 1 130 4011 30 50 185H9 32h12 34h8 2 130 395h9 30 50 185D10...
36902. Изучение среды и простейших элементов 405.5 KB
  Домашнее задание выполняется по различным вариантам. В данном варианте меняется только цвет фона всей формы и цвет фона окна Text3. Варианты индивидуальных заданий. Разработать Windowsприложение вычисления значения функции у средствами Visul Bsic Вариант №1 у = b^2 c^2 t^2 Вариант №2 y = bc^3 c t^2 Вариант №3 y = b^3 c t^2 Вариант №4 y = c3 t c^2 Вариант №5 y = c^2 b t^2 Вариант №6 y = tk^5 c b^3 Вариант №7 y = c^3 t^2 b^5 Вариант №8 y = c^2 t b^2 Вариант №9 y = c^3 t b^2...
36903. Разработка приложений с разветвляющимися алгоритмами 359 KB
  Lbel1 Cption При х = Lbel2 Cption Функция вычисляется по формуле: Lbel3 Cption Получен результат Y = Lbel4 Cption Lbel5 Cption Лабораторная работа 2.Вариант 37 Text1 Text Text2...
36904. Изучение основных явлений поляризации света 483 KB
  Изучение основных явлений поляризации света. Цель работы: Получение и исследование поляризованного света и исследование свойств обыкновенных и необыкновенных лучей полученных с помощью двояко преломляющего кристалла. Принципиальная схема установки или её главных узлов: 1 упражнение: 2 упражнение: ИС источник света; ИС источник света; П поляроид 1поляризатор; Д...