Шрифт:
Вывод программы 13.14 Копируемая строка Строка-константа Операции с указателями
Мы можем добавлять или вычитать целые значения из указателей. Два указателя можно сравнивать. Помимо этих операций, два указателя одного типа можно вычитать. Результатом вычитания двух указателей в Objective-C является число элементов, содержащихся между этими двумя указателями. Так, если а указывает на массив элементов какого-либо типа, а b указывает на другой элемент с более высоким номером в том же массиве, то выражение b - а представляет число элементов между этими двумя указателями. Например, если р указывает на какой-либо элемент в массиве х, то оператор n = р - х;
присваивает переменной п (в предположении, что это целая переменная) индекс элемента внутри массива х, на который указывает р. Следовательно, если р указывает на 100-й элемент в х р = &х[99];
то значение п после вычитания будет равно 99. Указатели на функции
Для полноты изложения введем чуть более сложное понятие указателя на фун-кцию. При работе с указателями на функции компилятору Objective-C требуется знать не только то, что указатель указывает на функцию, но и тип значения, возвращаемого этой функцией, число и типы ее аргументов. Чтобы объявить переменную fnPtr как указатель на функцию, которая возвращает значение типа int и не требует никаких аргументов, нужно написать строку int (*fnPtr) (void);
Круглые скобки вокруг fnPtr обязательны; в противном случае компилятор Objective-C будет интерпретировать этот оператор как объявление функции с именем fnPtr, которая возвращает указатель на тип int (оператор вызова функции имеет более высокий приоритет, чем оператор косвенного обращения через указатель ).
Чтобы задать, что указатель указывает определенную функцию, достаточно присвоить ему имя этой функции. Например, если lookup — это функция, которая возвращает значение типа int и не требует никаких аргументов, то оператор fnPtr = lookup;
сохраняет указатель на эту функцию в переменной-указателе функции fnPtr. Написание имени функции без последующих круглых скобок интерпретируется как имя массива без индекса. Компилятор Objective-C автоматически создает указатель на указанную функцию. Перед именем функции можно поставить амперсанд, но это не обязательно.
Если функция lookup не была ранее определена в программе, ее следует объявить до приведенного выше оператора, например, int lookup (void);
Для косвенного обращения к этой функции через переменную-указатель нужно применить к этому указателю оператор вызова функций с указанием всех аргументов функции в круглых скобках. Например, entry = fnPtr;
вызывает функцию, на которую указывает fnPtr, и возвращаемое значение со-храняется в переменной entry.
Указатели на функции часто перелаются в качестве аргументов другим фун-кциям. В библиотеке Standard Library функция qsort выполняет быструю сорти-ровку массива элементов данных. Эта функция принимает в качестве одного из своих аргументов указатель на функцию, которая вызывается, когда qsort требу-ется сравнение двух элементов в сортируемом массиве. Это позволяет исполь-зовать qsort для сортировки массивов любого типа, поскольку конкретное срав-нение любых двух элементов в массиве выполняется с помощью функции пользователя, а не самой функцией qsort.
В Foundation framework некоторые методы принимают в качестве аргумента указатель на функцию. Например, метод sortUsingFunction:context: определен в классе NSMutableArray и вызывает указанную функцию, когда требуется сравнение двух элементов массива.
Еще один применением указателей на функции является создание таблиц вызовов (dispatch table). М ы не можем сохранять сами функции в элементах мас-сива, но можем сохранять внутри массива указатели на функции. Это позволяет создавать таблицы, которые содержат указатели на вызываемые функции. Например, можно создать таблицу обработки различных команд, которые вводит пользователь. Каждая запись в таблице может содержать как имя команды, так и указатель на функцию, вызываемую для обработки этой конкретной команды. Когда пользователь вводит команду, ее можно найти в этой таблице и вызвать соответствующую функцию для ее обработки. Указатели и адреса памяти
Прежде чем закончить разговор об указателях в Objective-C, мы должны описать их реализацию. Память компьютера можно рассматривать как последовательный набор ячеек памяти. Каждая ячейка памяти компьютера имеет свой номер, называемый адресом. Обычно первый адрес памяти имеет номер 0. В большинстве компьютерных систем ячейка занимает 1 байт.
Компьютер использует память для хранения команд программы и хранения значений переменных, связанных с программой. Например, если мы объявим переменную с именем count типа int, то система выделит ячейки в памяти, чтобы сохранять значение count во время выполнения профаммы. Это может быть, например, адрес 1000FF16 в памяти компьютера.
К счастью, нам не нужно думать о конкретных адресах памяти, связанных с переменными, поскольку система делает это автоматически. Однако знание того, что каждая переменная связана со своим адресом в памяти, помогает понять, как действу ют указатели.
Когда мы применяем адресный оператор & к переменной в Objective-C, ге-нерируемое значение — это конкретный адрес данной переменной в памяти компьютера. (Именно поэтому мы называем этот оператор адресным.) Например, оператор intPtr = &count;