Программирование >>  Процедурные приложения 

1 ... 51 52 53 [ 54 ] 55 56 57 ... 150


Арифметические операции над указателями

Языки C/C++ дают возможность выполнять различные операции над указателями. В предыдущих примерах мы наблюдали, как адрес, хранящийся в указателе, или содержимое переменной, адресуемой указателем, присваивались другим указателям аналогичного типа. Кроме того, в C/C++ над указателями можно производить два математических действия: сложение и вычитание. Проиллюстрируем сказанное примером.

ptarith.cpp

Эта программа на языке C++ содержит примеры

арифметических в1ражений с участием указателей.

#include <iostream.h> void main (){ int *pi;

float *pf;

int an integer; float a real;

pi = &an integer; pf = &a real; pi++; pf++; }

Предположим, что в данной системе для целых чисел отводится 2 байта, а для чисел с плавающей запятой - 4 байта. Допустим также, что переменная an integer хранится в ячейке с адресом 2000, а переменная a real- в ячейке с адресом 4000. После выполнения двух последних строк программы значение указателя pi становится 2002, а указателя pf- 4004. Но, подождите, разве мы не знаем, что оператор ++ увеличивает значение переменной на единицу? Это справедливо для обычных переменных, но не для указателей.

В главе 4 мы познакомились с понятием перегрузки операторов. Операторы инкремента (++) и декремента (-) как раз являются примерами перегруженных операторов. Они модифицируют значение операнда-указателя на число, соответствующее размеру занимаемой им ячейки памяти. Для указателей типа int это 2, для указателей типа float-4 байта. Этот же принцип справедлив для указателей любых других типов. Если взять указатель, связанный со структурой размером 20 байтов, то единичное приращение такого указателя также будет соответствовать 20-ти байтам.

Адрес, хранимый в указателе, можно также изменять путем прибавления или вычитания любых целых чисел, а не только единицы, как в случае операторов ++ и -- . Например, чтобы сместить адрес на 4 ячейки, можно использовать такое выражение:

pf = pf + 4;

Рассмотрим следующую программу и попытаемся представить, каким будет результат ее выполнения.

sizept.cpp

Эта программа на языке C++ демонстрирует приращение указателя на значение, большее единицы.

#include <iostream.h> void main() {

float fvalues[] = (15.38,12.34,91.88,11.11,22.22);

float *pf;

size t fwidth;

pf = &fvalues [0];

fwidth = sizeof (float);

pf = pf + fwidth;

cout << *pf; }

Предположим, адрес переменной fvalue равен FFCA. Переменная fwidth принимает свое значение от оператора sizeof(float) (в нашем случае - 4). Что произойдет после выполнения предпоследней строки программы? Адрес в указателе pf поменяется на FFDA, а не на FFCE,



как можно было предположить. Почему? Не забывайте, что при выполнении арифметических действий с указателями нужно учитывать размер объекта, адресуемого данным указателем. В нашем случае величина приращения будет: 4x4 (размерность типа данных float) = 16. В результате указатель pfсместится на 4 ячейки массива, т.е. на значение 22,22.

Применение арифметики указателей при работе с массивами

Если вы знакомы с программированием на языке ассемблера, то наверняка сталкивались с задачей вычисления физических адресов элементов, хранящихся в массивах. При использовании индексов массивов в C/C++ выполняются те же самые операции. Разница состоит только в том, что в последнем случае функцию непосредственного обращения к адресам памяти берет на себя компилятор.

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

* arrayind.c

* Эта программа на языке С демонстрирует

* обращение к элементам массива по индексам.

#include <stdio.h>

#define ISIZE 10 void main () (

char string10[ISIZE];

int i;

for(i = 0; i < ISIZE; i++)

string10[i]= getchar(); for(i= ISIZE - 1; i >= 0; i--) putchar(string10[i]); ) }

Теперь приведем текст второй программы:

* arrayptr.c

* Эта программа на языке С демонстрирует обращение к

* элементам массива посредством указателей.

#include <stdio.h> #define ISIZE 10 void main () {

char string10[ISIZE];

char *pc; int icount; pc = string10;

for(icount = 0; icount < ISIZE; icount++) { *pc = getchar();

pc++; }

рс = stringlO + (ISIZE - 1);

for(icount = 0; icount < ISIZE; icount++)

{ putchar(*pc); pc-; }

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



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

рс = string10;

В цикле for программа считывает ряд символов, число которых определяется константой isize,и сохраняет их в массиве string10. Для занесения очередного символа по адресу, содержащемуся в указателе рс, применяется оператор *:

* рс = getchar() ;

В следующей строке значение указателя увеличивается на четыре с помощью операции инкрементирования (++). Таким способом осуществляется переход к следующему элементу массива.

Чтобы начать вывод символов массива в обратном порядке, следует в первую очередь присвоить указателю рс адрес последнего элемента массива:

рс = string10 + (ISIZE - 1);

Чтобы переместить указатель на 10-й элемент, мы прибавляем к базовому адресу массива значение 9, поскольку первый элемент имеет нулевое смещение.

Во втором цикле for указатель рс последовательно смещается от последнего к первому элементу массива с помощью операции декрементирования (--). Функция putchar() выводит на экран содержимое ячеек, адресуемых указателем.

Распространенная ошибка при использовании операторов ++ и

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

* рс++ = getchar (); *++рс = getchar ();

Первое выражение (с постинкрементом) присваивает символ, возвращаемый функцией getchar(), текущей ячейке, адресуемой указателем рс, после чего выполняется приращение указателя рс. Во втором выражении (с преинкрементом) сначала происходит приращение указателя рс, после чего в ячейку по обновленному адресу будет записан результат функции getchar(). Сказанное справедливо также для префиксной и постфиксной форм оператора - (декремент).

Применение квалификатора const совместно с указателями

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

const MYTYPE *pmytype l;

MYTYPE * const pmytype 2 = &mytype;

В первом случае переменная pmytype lобъявляется как указатель на константу типа mytype. Во втором случае pmytype 2 - это константный указатель на переменную mytype. Heпонятно?

Хорошо, давайте разбираться дальше. Идентификатор pmytype 1 является указателем. Указателю, как и любой переменной, можно присваивать значения соответствующего типа данных. В данном случае это должен быть адрес, по которому локализован объект типа mytype. А ключевое слово const в объявлении означает следующее: хотя указателю pmytype 1 может быть присвоен адрес любой ячейки памяти, содержащей данные типа mytype, впоследствии содержимое этой ячейки не может быть изменено путем обращения к указателю. Попробуем прояснить ситуацию на примере:

pmytype l = &mytypel; допустимо

pmytype l = &mytype2; допустимо

*pmytype l= (MYTYPE)float value; недопустимая попытка изменить значение защищенной ячейки памяти

Теперь рассмотрим переменную pmytype 2, которая объявлена как константный указатель. Она может содержать адрес ячейки памяти с данными типа mytype, но этот адрес недоступен для изменения. По этой причине инициализация константного указателя обязательно должна происходить одновременно с его объявлением (в приведенном выше примере указатель



1 ... 51 52 53 [ 54 ] 55 56 57 ... 150

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика