Понятие указателя. Адресная арифметика 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Понятие указателя. Адресная арифметика



 

В языке С++ существует два способа доступа к переменной: обращение к переменной по имени и использование механизма указателей.

Указатель-переменная (или просто указатель) – это переменная, предназначенная для хранения адреса в памяти.

Указатель-константа – это значение адреса оперативной памяти. В языке С++ определены две специальные операции для доступа к переменным через указатели: операция & и операция *. Результатом операции & является адрес объекта, к которому операция применяется. Например, &var1 дает адрес, по которому var1 хранится в памяти (точнее, адрес первого байта var1). Операция * - это операция обращения к содержимому памяти по адресу, хранимому в переменной-указателе или равному указателю-константе.

Признаком переменной-указателя для компилятора является наличие в описании переменной двух компонентов:

типа объекта данных, для доступа к которому используется указатель (т.е. на который ссылается указатель);

символа * перед именем переменной.

В совокупности тип и * воспринимаются компилятором как особый тип данных – «указатель на что-либо». Таким образом, описание

int var1, *ptr;

приводит к появлению переменной var1 и указателя-переменной ptr. Переменная var1 будет занимать два байта памяти. Указатель ptr имеет тип int* т.е. тип «указатель на целое». Место, выделяемое под такой тип компилятором, зависит от модели памяти. Указатели при их описании могут, как и обычные переменные, получать начальное значение. Например:

int var1, ptr1 = (int*)200, *ptr2 = &var1;

Здесь описаны две переменные-указатели ptr1 и ptr2; ptr1 получает начальное значение 200, а ptr2 в качестве начального значения – адрес, по которому в памяти хранится var1.

Операцию * можно выразить словами: «взять содержимое по адресу, равного значению указателя». Например, оператор присваивания

*ptr = *ptr2 + 4;

можно интерпретировать так: взять содержимое памяти по адресу, равному значению указателя ptr2, прибавить к этому содержимому 4, а результат поместить по адресу, равному значению указателя ptr1. Число байтов, извлекаемых из памяти и участвующих в операции, определяется компилятором исходя из типа, на который указывает указатель. Запись типа

*234 = var1;

будет ошибкой, т.к. в левой части оператора присваивания записывается адрес константы, а константа в С++ не имеет адреса в памяти.

Существуют ограничения и на использование операции взятия адреса &:

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

выражение

var1 = &0xff00;

2) нельзя определить адрес значения, получаемого при выполнении арифметического выражения, включающего знаки +, -, /, * и т.п. Например, некорректным является выражение

int var1, *ptr;

ptr = &(var1*3);

3) нельзя определить адрес переменной, описанной как register. Например, будет ошибкой попытка определить адрес var1:

unsigned register var1;

unsigned int *ptr;

ptr = &var1;

Сам указатель-переменная тоже имеет адрес. Поэтому, например, корректным будет такой фрагмент:

int var1, *ptr1, *ptr2 = &var1;

ptr1 = (int*)&ptr2;

Здесь описываются два указателя-переменные и ptr2 инициализируется значением адреса переменной var1. Затем ptr1 присваивается значение адреса, по которому в памяти располагается ptr2.

Указатель типа void* называют часто родовым (generic). Ключевое слово void говорит об отсутствии данных о размере объекта в памяти. Но компилятору для корректной интерпретации ссылки на память через указатель нужна информация о числе байтов, участвующих в операции. Поэтому во всех случаях использования указателя, описанного как void*, необходимо выполнить операцию явного приведения типа указателя. Например:

unsigned long block = 0xffeeddccL;

void *ptr = █

char ch;

unsigned two_bytes;

long int four_bytes;

ch = *(char*) ptr; /* ch = 0xcc; */

two_bytes = *(unsigned*) ptr; /* two_bytes = 0xddcc; */

four_bytes = *(long int*) ptr; /* four_bytes = 0xffeeddcc; */

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

Для указателей-переменных разрешены некоторые операции: присваивание; инкремент или декремент; сложение или вычитание; сравнение.

Язык С++ разрешает операцию сравнения указателей одинакового типа. При выполнении присваивания значение указателя в правой части выражения пересылается в ячейку памяти, отведенную для указателя в левой части.

Важной особенностью арифметических операций с указателями является то, что физическое увеличение или уменьшение его значения зависит от типа указателя, т.е. от размера того объекта, на который указатель ссылается. Если к указателю, описанному как type*ptr; прибавляется или отнимается константа N, значение ptr изменяется на N*sizeof(type). Разность двух указателей type*ptr1, *ptr2 – это разность их значений, поделенная на sizeof(type).

В частности, арифметические операции над указателями типа char* (размер типа равен 1) выполняются как над обычными целыми числами с той лишь разницей, что значения, участвующие в операции, - это адреса в оперативной памяти. Однако для других типов указателей это не так. Например:

#include <stdio.h>

void main(void)

{

int near*ptr1 = (int*)100;

int near*ptr2 = (int*)200;

ptr1 ++; ptr2 -= 10;

printf("ptr2 = %d, ptr1 = %d, ptr2 – ptr1 = %d\n", ptr2, ptr1, ptr2 – ptr1);

}

Так как указатель имеет тип int* (длина типа 2 байта), то «единица изменения» указателя и «единица измерения разности» равны двум байтам. Для других типов указателей такие же вычисления дают следующий результат:

для long* и float*

ptr2 = 160, ptr1 = 104, ptr2 – ptr1 = 14,

для double*

ptr2 = 120, ptr1 = 108, ptr2 – ptr1 = 1,

для long double*

ptr2 = 100, ptr1 = 110, ptr2 – ptr1 = -1.

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

 



Поделиться:


Последнее изменение этой страницы: 2017-02-05; просмотров: 336; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.142.96.146 (0.009 с.)