Главная » iPhone, Objective-C, Программирование для iOS и MacOS » Адреса и указатели Objective-C

0

В сущности, ваш компьютер состоит из процессора и оперативной памяти – огромного набора переключателей, которые могут включаться и выключаться процессором. Говорят, что каждый переключатель хранит 1 бит информации. Значение 1 обычно представляет «включенное» состояние, а значение 0 – «выключенное»

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

Рис 8.1. Память и процессор

Все ячейки памяти пронумерованы. Номер байта обычно называется его адресом. и когда речь идет о 32-разрядныx или 64-разрядных процессорах, имеется в виду величина адресов, используемых этими процессорами. 64-разрядный процессор способен работать с памятью намного, намного большего объема, чем 32-разрядныЙ.

Получение  адресов

Создайте в Хсоdе новый проект: программу командной строки С с именем Addresses.

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

оператор &:

#include <stdio.h>

int main(int argc, const char * argv[])

{

int i = 17;

printf("i stores its value at %p\n", &i);

return 0;

}

Обратите внимание на заполнитель %р – он используется при выводе адресов памяти. Постройте и запустите программу результат будет выглядеть примерно так:

i stores its value at 0xbffff738

Впрочем, ваш компьютер может разместить значение i по совершенно иному адресу. Адреса памяти почти всегда выводится в шестнадцатеричном формате.

На компьютере все данные хранятся в памяти, а следовательно, имеют адрес.

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

int main(int argc, const char * argv[])

{

int i = 17;

printf("i stores its value at %p\n", &i); printf("this function starts at %p\n", main); return 0;

}

Постройте и запустите программу.

Хранение адресов в указателях

А если адрес потребуется сохранить у переменной? Конечно, можно воспользоваться без знаковой целочисленной переменной подходящего размера, но если тип переменной будет указан более точно, компилятор поможет вам и укажет па возможные ошибки R программе. Например, если в переменной с именем ptr должен храниться адрес, по которому содержится значение типа float, ее объявление может выглядеть так

float *ptr;

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

Объявите новую переменную addressOfI, содержащую указатель на тип int.

Присвойте ей адрес i.

int main(int argc, const char * argv[])

{

int i = 17;

int *addressOfI = &i;

printf("i stores its value at %p\n", addressOfI);

printf("this function starts at %p\n", main); return 0;

}

Постройте и запустите программу. В ее повелении ровным счетом ничего не изменилось.

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

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

Обращение к данным по адресу

Если вам известен адрес данных, для обращения к самим данным можно воспользоваться оператором *. Следующая программа выводит на консоль значение целочисленной переменной, хранящейся по адресу addressofI:

int main(int argc, const char * argv[])

{

int i = 17;

int *addressOfI = &i;

printf("i stores its value at %p\n", addressOfI); printf("this function starts at %p\n", main);

printf("the int stored at addressOfI is %d\n", *addressOfI);

return 0;

}

Обратите внимание: звездочка (*) здесь используется двумя разными способами. Во-первых, переменная addressOfI объявляется с типом int*. Иначе говоря, объявляемая переменная является указателем на область памяти, в которой может храниться значение int.

Во-вторых, звездочка используется при чтении значения int, которое хранится по адресу, находящемуся addressOfI. Указатели также иногда называются ссылками, а использование указателя для чтения данных по адресу – разыменованием (derеfеrenсing) указателя.

Оператор * также может использоваться в левой части команды присваивания

для сохранения данных по указанному адресу:

int main(int argc, const char * argv[])

{

int i = 17;

int *addressOfI = &i;

printf("i stores its value at %p\n", addressOfI);

*addressOfI = 89; printf("Now i is %d\n", i); return 0;

}

Постройте и запустите программу.

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

А теперь совершим типичную ошибку программирования – уберем * из четвертой строки main(), чтобы строка приняла вид

addressOfI = 89;

Xcode выдает предупреждение о несовместимом преобразовании целого числа в указатель присваивании ‘int’ вместо ‘int *’.Исправьте ошибку.

Размер в байтах

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

Для  определения  размера  типа  данных  используется  функция  sizeof().

Пример:

int main(int argc, const char * argv[])

{

int i = 17;

int *addressOfI = &i;

printf("i stores its value at %p\n", addressOfI); *addressOfI = 89; printf("Now i is %d\n", i);

printf("An int is %zu bytes\n", sizeof(int)); printf("A pointer is %zu bytes\n", sizeof(int *)); return 0;

}

В вызовах printf() встречается новый заполнитель %zu. Функция sizeof() возвращает значение типа size_t, для вывода которого следует использовать относительно редкий заполнитель %zu.

Постройте и запустите программу. Если размер указателя составляет 4 байта, ваша программа выполняется в 32-разрядном режиме. Если же указатель занимает 8 байт, программа выполняется в 64-разрядном режиме.

В аргументе функции sizeof() также может передаваться переменная, так что приведенная выше программа может быть записана в таком виде:

int main(int argc, const char * argv[])

{

int i = 17;

int *addressOfI = &i;

printf("i stores its value at %p\n", addressOfI);

*addressOfI = 89; printf("Now i is %d\n", i);

printf("An int is %zu bytes\n", sizeof(i));

printf("A pointer is %zu bytes\n", sizeof(addressOfI)); return 0;

}

Источник: Аарон Хилегас, «Objective-C. Программирование для iOS и MacOS», 2012 г.

По теме:

  • Комментарии