- •Пространство имен.
- •Представление целых чисел
- •Определение переменных
- •Особенности использования некоторых типов данных: переполнение регистров переменных
- •Базовые конструкции структурного программирования
- •Условный оператор if
- •Ветвление процесса выполнения программ
- •Тема: Указатели и ссылки
- •Адреса переменных. Оператор взятия адреса
- •Указатели
- •Динамически распределяемая память
- •Указатели на объекты
- •Выделяют два основных отличия между статическими и динамическими объектами.
- •Перегрузка функций
- •Значения параметров функции, используемые по умолчанию
- •Определение массива
- •“Sdjdkjdhkfsdjhvm,c,bnmsierhoerhsdklfhbnasmbf”; //правильно Пустая строка записывается “ ” и имеет тип const char[1]. Использование датчика случайных чисел для формирования массива
- •Конструкторы и деструкторы
Тема: Указатели и ссылки
Вопрос №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.
Итак, указатели – это переменные, которые содержат в качестве своих значений адреса ячеек памяти.
С другой стороны указатель содержит адрес переменной, которая в свою очередь имеет определенное значение. В этом смысле переменная отсылает к своему значению прямо, а указатель – косвенно.
Ссылка на значение, содержащееся в переменной, посредством указателя называется косвенной адресацией.
Когда компилятор обрабатывает оператор определения переменной, (например, 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;
}