Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Материалы(С++).doc
Скачиваний:
6
Добавлен:
17.11.2019
Размер:
385.54 Кб
Скачать

Тема: Указатели и ссылки

Вопрос №1 Понятие указателя

Адреса переменных. Оператор взятия адреса

Задача. Создать функцию, предназначенную для обмена значений двух однотипных переменных.

// Решение

#include <iostream>

using namespace std;

void Obmen(int x, int y)

{

int temp;

temp=x;

x=y;

y=temp;

// обмен выполнен

cout<<"В Obmen () x = "<<x<<endl; // здесь x = 21

cout<<"В Obmen () y = "<<y<<endl; // здесь y = 7

}

void main()

{

int a = 7, b=21;

cout<<"В main() до вызова Obmen ()… a = "<<a<<endl;

cout<<"В main() до вызова Obmen ()… b = "<<b<<endl;

Obmen (a,b); //вызов

cout<<"В main() после вызова Obmen () a = "<<a<<endl;

cout<<"В main() после вызова Obmen () b = "<<b<<endl;

}

Результат:

В main()до вызова Obmen () a = 7

В main()до вызова Obmen () b = 21

В Obmen () x = 21

В Obmen () y = 7

В main() после вызова Obmen () a = 7

В main() после вызова Obmen () b = 21

Задача не решена, функция Obmen () не влияет на значение переменных a и b в основной программе. Переменные a, b и параметры функции Obmen (int x, int y) «соприкасаются» лишь в момент ее вызова, и внутри функции x = 21, y = 7 (обмен выполнен). Но когда происходит возврат в основную программу, переменные x и y внутри функции пропадают, а переменные a и b основной программы остаются теми же.

Решение данной проблемы – использование в качестве передаваемых параметров функции Obmen () аргументов не переменных a и b, а их адресов.

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

// Фрагмент программы.

// Использование оператора обращения к адресу переменной

unsigned short a = 5;

//получаем значение адресов переменных

cout<<"Adres a: "<<&a<<"\n";

Результат:

Adres a: 0012FF7C

Указатели

Для сохранения значения адреса используется специальный тип переменной – указатель.

Указатель (pointer) – это переменная, значением которой является адрес другой переменной, другого объекта или функции в памяти компьютера и позволяющая косвенно манипулировать ими.

Для объявления указателя используется символ звездочка:

тип_переменной * имя_указателя;

Здесь тип_переменной – это тип, на который ссылается указатель. Например:

int * ptr ; //переменная ptr объявляется указателем на тип int

Эта строка означает, что ptr – указатель, который будет содержать адрес переменной типа int.

Инициализация указателей

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

тип_переменной * имя_указателя = инициализатор;

Существуют следующие способы инициализации указателя:

1. Присвоение указателю адреса существующего объекта или переменной:

  • с помощью операции получения адреса. Чтобы присвоить указателю адрес переменной, перед ее именем ставится оператор взятия адреса (&):

тип_переменной * имя_указателя = & имя_переменной;

Пример

// фрагмент кода

int a = 7;

cout<<"Znachenie a = "<<a <<endl;

int *ptr=&a; // объявляется указатель и инициализируется

// значением адреса переменной а

*ptr = 5; //значение переменной – корректное значение указателя

cout<<" Znachenie a = "<<a <<endl;

Результат:

Znachenie a = 7

Znachenie a = 5

Связь между значением переменной и указателем

Обратите внимание, что как только объявляется указатель и в нем оказывается адрес переменной a, возникает «тайная тропа» к ней и после инструкции *ptr = 5; переменная становится равной 5.

  1. Итак, указатели – это переменные, которые содержат в качестве своих значений адреса ячеек памяти.

  2. С другой стороны указатель содержит адрес переменной, которая в свою очередь имеет определенное значение. В этом смысле переменная отсылает к своему значению прямо, а указатель – косвенно.

Ссылка на значение, содержащееся в переменной, посредством указателя называется косвенной адресацией.

Когда компилятор обрабатывает оператор определения переменной, (например, int i=10;), то в памяти выделяется участок в соответствии с типом переменной (для int 4 байта) и записывает в этот участок указанное значение.

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

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

Чтобы возвратить значение по адресу, содержащемуся в указателе, перед его именем устанавливается оператор разыменования или косвенного обращения к объекту – звездочка (*):

тип_переменной имя_переменной = *имя_указателя;

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

// Пример_2. (фрагмент программы)

void main()

{

int a = 10; // определение переменной a

int *p=&a; // инициализация указателя

*ptr=*ptr+11; // косвенный доступ к а и неявное увеличение ее

// значения

cout<<"Znachenie a = "<< a <<endl;

}

Результат:

Znachenie a = 21

Как видно действие оператора *ptr=*ptr+11; аналогично оператору а =а+11;, а запись *ptr в контексте оператора *ptr=*ptr+11; по существу обозначает саму переменную a.

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

Форматы использования оператора разыменования

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

*указатель

ведет себя по-разному, в зависимости от своего местоположения по отношению к оператору присваивания:

  • с

    … = *ptr

    права от оператора присваивания, что означает «взять значение переменной, адрес которой содержит указатель, и присвоить его какой-либо переменной». Например,

//Пример.

#include <iostream>

using namespace std;

int main()

{

int a,b,c;

int *ptr=NULL; //указатель инициализирован пустым значением

a=7;

b=21;

ptr=&a;

c=*ptr;

cout<<"Value c: "<<c<<endl<<endl; //c=7

//конструкция c = *ptr означает c = *(&a) или

//что то же самое c=a – т.е. (*) и (&) взаимно стираются

c=*ptr+21;

cout<<"Value c: "<<c<<endl<<endl; //c=28

ptr=&b;

c=*ptr;

cout<<"Value c: "<<c<<endl<<endl; //c=21

return 0;

}

*ptr = …

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

// Пример.

#include <iostream>

using namespace std;

int main()

{

int b;

int *ptr;

ptr=&b;

*ptr=21;

//конструкция *ptr = 21 означает *(&b) = 21 или

//что то же самое b=21 – т.е. (&) и (*) взаимно стираются

cout<<"Value b: "<<b<<endl<<endl; //b=21

return 0;

}