Указатели
Указатель - это переменная, содержащая адрес области памяти. Указатели широко применяются в языке С++. В некоторых случаях без них просто не обойтись, а в некоторых программы с использованием указателей становятся короче и эффективнее.
Начнем с того, что поговорим о структуре памяти любого компьютера. Как известно, память компьютера представляет последовательность 8-битовых байтов. Каждый байт пронумерован, причем нумерация начинается с нуля. Номер байта называется адресом. Иногда говорят, что адрес указывает на определенный байт. Таким образом, указатель является просто адресом байта памяти.
Язык С++ позволяет определять переменные, которые могут хранить адреса памяти. Такие переменные и называются указателями. Значение указателя сообщает о том, где размещен объект, но ничего не говорит о значении самого объекта.
Указатель |
Объект Данные или группа данных |
Присваивая указателю то или иное допустимое значение, можно обеспечить доступ к данным через этот указатель.
Для описания переменной типа указатель используется символ *.
Формат описания:
Тип *имя;
Указатель – адрес переменной какого-то определенного типа. Этот тип сообщается компилятору при объявлении указателя.
int *x;
char *y;
Пример следует понимать так: x – это указатель на ячейку, в которой хранится целое значение, а y – указатель на однобайтовую ячейку, предназначенную для хранения символа.
Двумя наиболее важными операциями, связанными с указателями, являются операция обращения по адресу * и определение адреса &.
Операция обращения по адресу предназначена для записи или считывания значения, размещенного по адресу, содержащемуся в переменной-указателе.
Например:
int *x; //объявление указателя х
. . .
*x=5; //запись по адресу х значения 5
Операция определения адреса & возвращает адрес памяти своего операнда. Операндом должна быть переменная.
Например:
int *x; // объявление указателя х
int a=5; //объявление и инициализация переменной а
x=&a; //копирование адреса переменной а в х
Кроме того, над указателями можно выполнять арифметические операции сложения и вычитания.
Если у – указатель на целое, то унарная операция y++ увеличивает значение адреса, хранящегося в переменной-указателе на число равное размеру ячейки целого типа, т.е. на 2 байта для 16-разрядного процессора и на 4 байта – для 32-разрядного; теперь оно является адресом следующей ячейки целого типа. Соответственно, оператор y--; означает, что адрес уменьшается аналогично на 2 или на 4 байта.
Указатели и целые числа можно складывать. Конструкция у + n (у - указатель, n - целое число) задает адрес n-гo объекта, на который указывает у. Это справедливо для любых объектов (int, char, float и др.); транслятор будет масштабировать приращение адреса в соответствии с типом, указанным в определении объекта.
Любой адрес можно проверить на равенство (==) или неравенство (!=), больше (>) или меньше (<) с любым другим адресом.
Рассмотрим следующий фрагмент программы:
#include "stdafx.h"
int main()
{
int *x, *w;
int y;
*x=18;
y=-15;
w=&y;
. . .
}
Этот текст можно понимать так:
Выделить память под три переменные x, w, y, где x и w –переменные типа указатель. Оператор *x=18; означает, что в ячейку, адрес которой записан в x, помещается значение 18. Затем переменной y присваивается значение -15. После чего в указатель w записывается адрес переменной y. Синтаксически текст записан правильно. Проблема заключается в том, что указатель x не инициализирован. Описание int *x; это лишь указание компилятору резервировать память, необходимую для хранения адреса целой ячейки. Но в этой памяти может оказаться адрес любой ячейки, в том числе и адрес, где хранится полезная информация, например, операционная система. Запись в такую ячейку может привести к сбою в работе компьютера. Поэтому при работе с указателями их надо правильно инициализировать. Существует 4 способа правильного задания начального значения для указателя:
1) Описать указатель глобально, т.е. вне любой функции. При этом указатель будет инициализирован безопасным нулевым адресом. Кроме того любому указателю можно присвоить безопасный нулевой адрес, например:
int *x;
x=NULL;
Гарантируется, что этот адрес не совпадет ни с одним адресом, уже использованным в системе.
2) Присвоить указателю адрес переменной. Например: w=&y;
3) Присвоить указателю значение другого указателя, к этому моменту правильно инициализированного. Например: x=w;
4) Использовать функции выделения динамической памяти malloc() или calloc(). При использовании этих функция необходимо подключать библиотеку <malloc.h>. Рассмотрим пример использования функции malloc():
x=(int*)malloc(sizeof(int));
Приведенный пример означает, что функция выделит область памяти, размер которой определит функция sizeof(). Если вы знаете, что размер ячейки типа int равен 2 байтам, то можно написать проще: x=(int*)malloc(2);
По окончанию работы программы, память, выделенную функцией malloc() рекомендуется освободить функцией free(x); Вернемся к приведенному ранее фрагменту программы:
#include "stdafx.h"
#include <malloc.h>
int main()
{ int *x, *w;
int y;
x=(int*)malloc(sizeof(int));
*x=16;
y=-15;
w=&y;
. . .
}
Теперь никаких конфликтных ситуаций при работе с указателями не возникнет. В языке С++ существует еще одна пара операторов new и delete для динамического выделения и освобождения памяти. О них мы поговорим чуть позже.
Дата добавления: 2015-02-10; просмотров: 966;