Главная
Регистрация
Вход
Пятница
26.04.2024
03:11
Приветствую Вас, Гость | RSS

Меню сайта

Друзья сайта

Категории раздела
1. Основы работы в среде C++ Builder.Основы языка Си++ [9]
2. Операторы ветвления и операторы передачи управления [4]
3. Операторы цикла и операторы передачи управления [5]
4. Одномерные массивы [3]
5. Многомерные массивы [3]
6. Указатели.Динамическое распределение памяти [6]
7. Отладка программы [3]
8. Функции [8]
9. Строки [5]
10. Функция Main [2]
11. Типы данных,определяемые пользователем [5]
12. Потоки ввода/вывода [7]
13. Основы объектно-ориентированного программирования [5]
Приложения [11]

Форма входа

Наш опрос
Как вам новый дизайн сайта?
Всего ответов: 463

Пользователи

Онлайн всего: 5
Гостей: 5
Пользователей: 0

 Каталог статей 
Главная » Статьи » Лекции по C++ » 6. Указатели.Динамическое распределение памяти

PDA-версия страницы

1. Указатели

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

Для описания указателей используется операция косвенной адресации *. Например, указатель целого типа uk описывается так :

 int *uk.

Унарная операция &, примененная к некоторой переменной, показывает, что нам нужен адрес этой переменной, а не ее текущее значение. Если переменная uk объявлена как указатель, то оператор присваивания

 uk=&x

означает: "взять адрес переменной x и присвоить его значение переменной указателю uk". Теперь к переменной x можно обратиться как *uk.

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

Чаще, указатели используются при работе с массивами, символьными строками, зарезервированными областями памяти и объектами, память для которых выделяется динамически во время выполнения программы (п. 2.3).

Итак, указатели предназначены для хранения адресов областей памяти. В C++ различают три вида указателей – указатели на объект, на функцию и на void, отличающиеся свойствами и набором допустимых операций. Указатель не является самостоятельным типом, он всегда связан с каким-либо другим конкретным типом объекта.

Указатель на объект содержит адрес области памяти, в которой хранятся данные определенного типа (основного или составного). Объявление указателя на объект (в дальнейшем называемого просто указателем) имеет вид:

тип   *имя;

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

Пример объявления указателей на переменные целого типа:

int  *pi, *pbi, *pci;

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

Существует соглашение: имя указателя начинать с буквы p. Это облегчает чтение программы.

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

тип   *имя указателя = инициализирующее выражение;

тип   *имя указателя (инициализирующее выражение);

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

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

   – с помощью операции получения адреса:

int a = 5; // целая переменная

int *р = &а; //в указатель записывается адрес а

int *р (&а); // в указатель записывается адрес а другим способом

   – с помощью значения другого инициализированного указателя p:

int a = 5;

int *р = &а;

int *pr = р; //pr тоже указатель на a

   – с помощью имени массива :

int b[10]; // массив

int * pb = b; // присваивание адреса первого элемента массива

2. Присваивание указателю адреса области памяти в явном виде:

char *pv = (char *)0xB8000000;

Здесь 0xB8000000 – шестнадцатеричная константа (начальный адрес видеопамяти ПЭВМ ), (char *) – обязательная операция приведения типа: константа преобразуется к типу указателя ( char *). Таким образом, определяется, что в эту ячейку памяти будет записан код переменной типа char.

3. Присваивание пустого значения:

int *psuxx = NULL;

int *prulez = 0;

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

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

int a; // целая переменная

int *р = &а; //в указатель записывается адрес а

*p = 5; //через операцию разыменования указателя переменной a присвоено значение 5

cout<< *p; // вывод значения переменной a через указатель

но

cout<< p; // вывод адреса переменной a

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

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

char *pv = (char *)0xB8000000;// присваивание указателю адреса области памяти в явном виде

*pv = '+';    // запись в ячейку с адресом 0xB8000000 символа  +

char v = *pv;       // присваивание v значения из ячейки с адресом

0xB8000000, значение будет представлено в символьном виде

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

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

Пример:

char  *const key_byte = (char*)1047;

Значение указателя key_byte невозможно изменить, он всегда указывает на байт с адресом 1047.

Содержимое участка памяти связанного с константным указателем с помощью разыменования можно читать и изменять.

 

char  *const key_byte = (char*)1047;

    cout<<"\nbyte key: "<< *key_byte;

*key_byte = 'Ё';

    cout<<"\nbyte key: "<< *key_byte;

 

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

key_byte = NULL;

не допустит компилятор и выдаст сообщение об ошибке.

Можно определить указатель на константу. Формат определения:

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

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

const int zero = 0; //определение константы

int const *pconst = &zero; //указатель на константу 0

Операторы вида

*pconst = 1;

cin>>*pconst;

недопустимы, так как каждый из них – попытка изменить значение константы 0.

Однако операторы

pconst = &a;

pconst = NULL;

допустимы, так как разрывают связь указателя pconst с константой 0, но не меняют значение этой константы.

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

const float pi = 3.141593;

float const *const ppi = &pi;

Можно определить указатель на указатель и т.д. сколько нужно раз. В следующей программе определены такие указатели и с их помощью выполнен доступ к значению переменной:

 

int i = 77;

int *pi = &i;

int **ppi = &pi;

int ***pppi = &ppi;

cout<<"i = "<< ***pppi;

 

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

Указателю на void можно присвоить значение указателя любого типа, а также сравнивать его с любыми указателями, но перед выполнением каких-либо действий с областью памяти, на которую он ссылается, требуется преобразовать его к конкретному типу явным образом. Возможности связывания указателя  void * с объектами разных типов показаны в следующей программе:

 

void *pv;

int i = 77;

float f = 2.3456;

cout<<RUS("\nНачальное значение pv = ")<< pv;

pv = &i;     //работаем с переменной типа int

cout<<"i = "<< *(int *)pv;     //перед разыменованием явное приведение типа указателя к типу int

pv = &f;     // работаем с переменной типа float

cout<<"f = "<< *(float *)pv;  //перед разыменованием явное приведение типа указателя к типу float

Категория: 6. Указатели.Динамическое распределение памяти | Добавил: Admin (01.09.2011)
Просмотров: 15382 | Рейтинг: 4.0/3
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]

Помочь сайту!
рублей WMR
WebMoney на кошелек R407507742179

Партнёры сайта

Закладки

Поиск по сайту

Copyright H-L-L.RU © 2024
Сделать бесплатный сайт с uCoz