Программирование на Objective-C 2.0
Шрифт:
сначала извлекается символ, на который указывает textPtr, а затем увеличивается его значение. В обоих случаях круглые скобки не обязательны, поскольку опера-торы * и ++ имеют одинаковый приоритет, но применяются справа налево.
Вернемся к функции copyString из программы 13.13 и перепишем ее для включения операций прирашения непосредственно в оператор присваивания.
Поскольку указатели to и from наращиваются каждый раз после выполнения оператора присваивания внутри цикла for, их следует включить в оператор при-сваивания как операции пост-увеличения. Модифицированный цикл for из про-граммы 13.13 принимает вид for (; *from != '\0';) *to++ = *from++;
Выполнение оператора присваивания внутри никла происходит следующим образом.
Приведенный цикл не слишком пригоден, поскольку не имеет начального выражения и не содержит выражения цикла. Для этой логики больше подходит цикл while. Он показан в программе 13.14, где представлена новая версия функции copyStriog. В этом цикле while учитывается, что нуль-символ равен значению 0. // Функция для копирования одной строки в другую // с помощью указателей, версия 2 #import <Foundation/Foundation.h> void copyString (char *to, char *from) { while (*from) *to++ = *from++; *to = “\0"; } int main (int arge, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; void copyString (char *to, char *from); char string 1[] = "Копируемая строка"; char string2[50]; copyString (string2, string 1); NSLog (@"%s", string2); copyString (string2, "Строка-константа"); NSLog (@"%s", string2); [pool drain]; return 0; }
Вывод программы 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;
присваивает указателю intPtr адрес в памяти компьютера, который был выделен для переменной count. Так, если count размешена по адресу 1000FF6, этот опера-тор присвоит указателю intPtr значение OxlOOOFF.
Применение оператора косвенного доступа * к переменной-указателю, как в выражении *intPtr
означает интерпретацию значения, содержащегося в этой переменной, как адреса памяти. Значение, хранящееся по этому адресу, считывается и интерпретируется в соответствии с типом, объявленным для этой переменной. Например, если переменная intPtr является указателем на тип int, то система будет интерпретировать значение, хранящееся по адресу памяти, заданному с помощью *intPtr, как целое значение. 13.5. Объединения