C Урок 27. Адресная арифметика. Часть 1



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

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

Какие же действия мы можем производить над адресами и указателями?

Мы сегодня рассмотрим основные действия. Может быть, есть ещё какие-то действия, но, скорее всего, они вытекающие из тех, которые мы сегодня с вами рассмотрим.

Вот основные действия:

  • инкрементирование указателя (pointer increment),
  • декрементирование указателя (pointer decrement),
  • сложение указателя с константой (constant addition),
  • вычитание константы из значения указателя (constant subtraction),
  • вычитание одного указателя из другого (subtraction for two pointers).

Также затем (только в практической части) мы поиграемся ещё над некоторыми действиями с указателями, что, надеюсь будет вам интересно, а также (я уверен в этом) поможет вам в дальнейшем в проектировании ваших будущих работ.

Давайте теперь немного поподробнее познакомимся с арифметическими операциями над указателями, а затем уже в практической части мы поймём ещё и смысл данных операций.

Прежде чем начнём рассмотрение всех названных арифметический действий над указателями, давайте объявим, например, вот такой вот массив из 10 целочисленных беззнаковых 32-разрядных элементов

 

unsigned int a[10] = {0x33333333, 0x44444444, 0x55555555, 0x66666666, 0x77777777, \

0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC};

 

Объявим два уазателя на такой же тип и присвоим им адреса начала нашего массива, а также, например, пятого элемента того же массива. Как это делается, мы уже знаем

 

unsigned int *p0_a, *p5_a;

p0_a = &a[0];

p5_a = &a[5];

 

Получится у нас в памяти примерно вот такая картина

 

 

Переменная указатель с именем p0_a у нас указыват на нулевой элемент, а p5_a — на пятый элемент массива.

Вот теперь мы попробуем с данными указателями как-то поиграть, используя арифметические операции.

Инкрементирование указателя (pointer increment)

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

То есть, если у нас тип указателя unsigned int *, то это значит, что адрес, на который указывает указатель, переместится в памяти на столько байтов, сколько предусматривает его тип, а не на один байт.

Попробуем проинкрементировать наши оба указателя

 

p0_a++; p5_a++;

 

После этого указатели будут содержать адреса следующих элементов массива в памяти за теми, на которые они показывали до операции

 

 

Дерементирование указателя (pointer decrement).

Это операция, обратная операции инкрементирования.

 

 

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

Попробуем теперь продекрементировать наши указатели

 

p0_a--; p5_a--;

 

Соответственно, после такой операции у нас указатели приобретут те же значения, которые были до инкрементирования, то есть, простыми словами, вернутся на место

 

 

Сложение указателя с константой (constant addition).

Теперь попробуем прибавить к значениям указателей какие-нибудь константы

 

p0_a+=4; p5_a+=2;

 

Я думаю, мы все поняли, что указатели наши переместятся теперь на столько позиций в памяти, чему равны константы, которые мы к ним прибавляем и будут показывать уже вот на эти элементы массива

 

 

Вычитание константы из значения указателя (constant subtraction).

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

 

 

Попробуем произвести такие операции над нашими указателями

 

p0_a-=4; p5_a-=2;

 

Так как мы использовали те же константы, что и при сложении, то наши указатели просто вернутся на свои места

 

 

Вычитание одного указателя из другого (subtraction for two pointers).

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

Если мы применим данную операцию

 

unsigned int n = p5_a - p0_a;

 

то n у нас будет равен 5, так как между пятой и нулевой ячейкой у нас 5 позиций.

Соответственно, сложение указателей не применяется, так как не имеет смысла.

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

Проект мы также создадим из проекта прошлого урока с именем MYPROG26 и дадим ему имя MYPROG27.

Откроем наш проект в Eclipse, произведём его первоначальную настройку и удалим весь наш код из функции main() за исключением возврата. Функция main() приобретёт вот такой вид

 

int main()

{

   return 0; //Return an integer from a function

}

 

Объявим и проинициализируем массив из 10 элементов типа unsigned char

 

 

Путём применения нехитрых операций выведем адреса элементов массива и также значения данных элементов

 

 

Посмотрим, как это выглядит в консоли

 

 

Очень даже неплохо.

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

 

 

Соберём код и посмотрим, как это будет выглядеть

 

 

Применим операции инкрементирования к обоим указателям и выведем результат этих операций в консоль

 

 

Соберём код и посмотрим, что у нас изменилось в указателях

 

 

К нашим указателям добавилось по четвёрке, так как у нас типы четырёхбайтные и они уже показывают значения элементов при разыменовании, находящихся на позицию выше в памяти (выше — это не по нашему рисунку, а по арифметическим соображениям, то есть больше).

Теперь применим к нашим указателям декрементирование

 

 

Посмотрим результаты данных операций

 

 

Как видим, у нас всё вернулось на свои места.

Теперь прибавим константы к нашим указателям

 

 

Посмотрим результаты операций

 

Указатели сместились на значения констант (ну, конечно же, умноженных на 4).

Теперь отнимем от наших указателей те же константы

 

 

Посмотрим результат

 

 

Указатели вернулись на место.

А теперь с помощью опрерации вычитания указателей найдём значение, на котором они находятся друг от друга в памяти. Значение, как мы знаем, мы должны получить не в байтах, а в ячейках, равных по размеру типам наших указателей

 

 

Проверим, так ли это

 

 

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

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

 

 

Предыдущий урок Программирование на C Следующая часть

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

C Адресная арифметика

2 комментария на “C Урок 27. Адресная арифметика. Часть 1
  1. Лия:

    Вы так подробно объясняете!
    Невероятно Вам благодарна! ❤️

  2. Артем:

    А исходный код можно выложить, мне он очень помогает время сэкономить. Кстати, за видео материал отдельное спасибо!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*