355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Стивен Кочан » Программирование на Objective-C 2.0 » Текст книги (страница 16)
Программирование на Objective-C 2.0
  • Текст добавлен: 19 сентября 2016, 13:02

Текст книги "Программирование на Objective-C 2.0"


Автор книги: Стивен Кочан



сообщить о нарушении

Текущая страница: 16 (всего у книги 32 страниц)

задаст косвенную связь между intPtr и count. Адресный оператор 8 присваивает переменной intPtr не значение переменной count, а указатель на переменную count. На рис. 13.1 показана связь между intPtr и count. Линия со стрелкой показывает, что intPtr содержит не само значение count, а указатель на переменную count.

Рис. 13.1. Указатель на переменную целого типа

Чтобы указать содержимое count с помощью переменной-указателя intPtr, ис-пользуется оператор косвенного обращения «звездочка» (*). Если переменная х была определена с типом int, то оператор х = *intPtr;

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

В программе 13.8 показано использование двух основных операторов для указателей: адресного оператора (&) и оператора косвенного обращения (*). // Программа, показывающая использование указателей #import int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int count = 10, x; int *intPtr; intPtr = &count; x = *intPtr; NSLog (@"count = %i, x = %i", count, x); [pool drain]; return 0; }

Вывод программы 13.8 count = 10, x = 10

Переменные count и x объявляются обычным образом как целые переменные. В следующей строке переменная intPtr к объявляется ак «указатель на тип int». Эти две строки можно было бы объединить в одну:  int count = 10, х, *intPtr;

После этого к переменной count применяется адресный оператор (&), результатом которого является создание указателя на эту переменную, который затем присваивается переменной intPtr.

Выполнение строки х = *intPtr;

происходит следующим образом. Оператор косвенного обращения () сообщает системе Objective-C, что переменная intPtr содержит указатель на другой элемент данных. Этот указатель используется для доступа к нужному элементу данных, тип которого задан при объявлении переменной-указателя. При объявлении переменной мы сообщили компилятору, что переменная intPtr указывает на целые элементы данных, поэтому компилятор «понимает», что значение, указываемое выражением intPtr, является целым. Кроме того, в предыдущей строке мы задали, что intPtr указывает на целую переменную count, поэтому это выражение представляет косвенный доступ к значению count.

В программе 13.9 иллюстрируются некоторые интересные свойства пере-менных-указателей. В ней используется указатель на символ. #import int main (int arge, char *argv[]) { NSAutoreleasePooi * pool = [[NSAutoreleasePool alloc] init]; char c = “Q"; char *charPtr = &c; NSLog (@"%c %c", c, *charPtr); c = '/'; NSLog (@"%c %c", c, *charPtr); *charPtr = T; NSLog (@“%c %c", c, *charPtr); [pool drain]; return 0; }

} Вывод программы 13.9 QQ // ((

Символьная переменная с определяется и инициализируется со значением 'Q'. В следующей строке этой программы определяется переменная charPtr как «указатель на тип char». Это означает, что любое значение, сохраняемое внутри этой переменной, следует интерпретировать как косвенное обращение (указатель) к символу. Этой переменной можно присвоить начальное значение обычным образом. В этой программе переменной charPtr присваивается указатель на переменную с, и для этого к переменной с применяется адресный оператор &. (Эта инициализация приведет к появлению ошибки компилятора, если определить с после этого оператора, поскольку любая переменная должна быть объявлена раньше ссылки на эту переменную в любом выражении.)

Объявление переменной charPtr и присваивание ее начального значения можно было бы представить в двух отдельных строках char *charPtr; charPtr = &с;

(Но не char *charPtr; *charPtr = &с;

как можно было бы предположить из объявления в одной строке.)

Значение указателя в Objective-C не существует, пока мы не зададим, на какой элемент данных он указывает.

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

В следующей строке профаммы символьной переменной с присваивается символ '/ Поскольку charPtr по-прежнему указывает на с, в следующем вызове NSLog для значения charPtr на терминал выводится новое значение переменной с. Это важно. Если значение charPtr не изменяется, то выражение charPtr всегда означает доступ к значению с. Таким образом, если изменяется значение пере-менной с, то изменяется и значение выражения *charPtr.

Мы говорили, что если значение charPtr не изменено, то выражение *charPtr всегда является ссылкой на значение с. Поэтому в выражении *charPtr = '(';

переменной с присваивается символ «левая круглая скобка». Более формально символ '(' присваивается переменной, на которую указывает charPtr. Мы знаем, что это переменная с, поскольку в начале профаммы мы присвоили charPtr указатель на с.

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

Мы определил указатель для базовых типов данных, таких как int или char. Можно определить указатель и на структуру. Выше в этой главе мы определили структуру dale. struct date { int month; int day; int year; };

Мы определили переменные типа struct date, например, struct date todaysDate;

Мы можем также определить переменную-указатель на переменную типа struct date: struct date *datePtr;

Переменная datePtr может, например, указывать на переменную todaysDate с помощью оператора присваивания datePtr = StodaysDate;

После этот присваивания можно осуществлять косвенный доступ к любому из компонентов структуры date: (*datePtr).day = 21;

С помощью указателя datePtr в этом операторе задается значение дня струк-туры date, равное 21. Здесь нужны круглые скобки, поскольку оператор «точка» для компонента структуры имеет более высокий приоритет, чем оператор кос-венного обращения *.

Чтобы проверить значение месяца (month), хранящееся в структуре date, на которую указывает datePtr, можно использовать оператор if ((*datePtr).month == 12 )

Указатели на структуры используются настолько часто, что для них в языке имеется специальный оператор. Оператор указателя структуры -> (знаки «минус» и «больше») позволяет записывать выражения (*х).у

в более понятном виде: х->у

Таким образом, предыдущий оператор if можно записать в виде if (datePtr->month == 12)

Перепишем программу 13.6, где впервые показали использование структур, с применением указателей структур (программа 13.10). // Программа, показывающая использование указателей структур #import int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; struct date { int month; int day; int year; }; struct date today, *datePtr; datePtr = &today; datePtr->month = 9; datePtr->day = 25; datePtr->year = 2009; NSLog (@"Today's date is %i/%i/%.2i.", datePtr->month, datePtr->day, datePtr->year % 100); (pool drain]; return 0; }

Вывод программы 13.10 Today's date is 9/25/09. (Текущая дата – 25.9.09) Указатели, методы и функции

Вы можете передавать указатель как аргумент методу или функции. Метод или функция может возвращать результат в виде указателя. Кстати, именно это про-исходит с методами alloc и init: они возвращают указатели. Более подробно мы обсудим этот вопрос в конце главы. А теперь рассмотрим программу 13.11. // Указатели как аргументы, передаваемые функциям #import void exchange (int *pint1, int *pint2) { int temp; temp = *pint1; *pint1 = *pint2; *pint2 = temp; } int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; void exchange (int *pint1, int *pint2); int И = -5, i2 = 66, *p1 = &i1, *p2 = &i2; NSLog (@"i1 = %i, i2 = %i", i1, i2); exchange (p1, p2); NSLog (@"i1 = %i, i2 = %i", i1, i2); exchange (&i1, &i2); NSLog (@"i1 = %i, i2 = %i", i1, i2); [pool drain]; return 0; }

Вывод программы 13.11 i1 =-5, i2 = 66 i1 = 66, i2 = -5 i1 = -5, i2 = 66

Функция exchange выполняет обмен значений двух целых переменных, на которые указывают два ее аргумента. Внутри функции локальная целая переменная temp сохраняет одно из целых значений во время выполнения обмена. Ей присваивается целое значение, на которое указывает pintl. Затем целое значение, на которое указывает pint2, копируется в целую переменную, на которую указывает pintl. После этого значение temp сохраняется в целой переменной, на которую указывает pint2, и обмен значениями завершается.

В процедуре main определяются целые переменные И и i2 со значениями -5 и 66 соответственно. Затем определяются два указателя на тип int (pi и р2), которые указывают соответственно на И и 12. Программа выводит значения И и i2 и вызывает функцию exchange, передавая в качестве аргументов эти указатели (р1 и р2). Функция exchange выполняет обмен значения, содержащегося в целой пе-ременной, на которую указывает pi, со значением, содержащимся в целой пе-ременной, на которую указывает р2. Поскольку р1 указывает на il и р2 на i2, функция exchange обменивает местами значения И и i2. Вывод с помошью второго вызова NSLog показывает, что обмен выполняется правильно.

Второй вызов exchange выглядит несколько интересней. На этот раз аргумен-ты, передаваемые функции, являются указателями на И и i2, которые создаются непосредственно при обращении в результате применения к этим переменным адресного оператора &. Поскольку выражение &И представляет указатель на целую переменную И, это согласуется с типом первого аргумента для функции (указатель на целое значение). То же самое относится ко второму аргументу. Вывод этой программы показывает, что функция exchange правильно выполнила свою работу и обменяла значения И и i2.

Внимательно изучите программу 13.11. Она показывает ключевые концепции работы с указателями в Objective-C. Указатели и массивы

Если имеется массив с именем values, содержащий 100 целых элементов, то с помощью следующей строки можно определить указатель с именем valuesPtr, который можно использовать для доступа к целым элементам, содержащимся в этом массиве. int *valuesPtr;

Определяя указатель, который будет использоваться для указания элементов массива, мы не определяем его как «указатель на массив». Он определяется как указатель на тип элементов, содержащихся в массиве.

Если имеется массив tracts с объектами класса Fraction, то с помощью следующего оператора можно определить указатель на элементы массива tracts: Fraction **fractsPtr;

Такое же объявление используется для определения любого объекта класса Fraction.

Чтобы valuesRr указывал на первый элемент массива values, достаточно напи-сать строку valuesPtr = values;

В данном случае адресный оператор & не используется, поскольку компилятор Objective-C интерпретирует появление имени массива без индекса как указатель на первый элемент этого массива. Таким образом, мы получаем указатель на первый элемент массива values.

Эквивалентный способ создать указатель на начало массива values – приме-нить адресный оператор & к первому элементу этого массива: valuesPtr = &values[0];

Чтобы вывести содержащийся в массиве tracts объект класса Fraction, на кото-рый указывает fractsPtr, нужно написать оператор [*fractsPtr print];

Реальные возможности применения указателей к массивам начинают дей-ствовать при необходимости перебора элементов массива. Если указатель valuesPtr определен, как описано выше, и указывает на первый элемент массива values, то для доступа к первому элементу массива values (то есть values[0]) можно использовать выражение *valuesPtr

Для доступа к values[3] с помощью переменной valuesPtr можно добавить 3 к valuesPtr и затем применить оператор косвенного доступа. *(valuesPtr + 3)

Таким образом, доступ к значению, содержащемуся в values[i], дает выраже-ние *(valuesPtr + i)

Например, чтобы присвоить элементу values[10] значение 27, можно написать следующее выражение values[10] = 27;

Или, используя valuesPtr, можно написать *(valuesPtr + 10) = 27;

Чтобы valuesPtr указывал на второй элемент массива values, нужно применить адресный оператор & к values[1] и присвоить результат переменной valuesPtr: valuesPtr = &values[1];

Если valuesPtr указывает на values[0], и нужно сделать так, чтобы он указывал на values[1], достаточно добавить 1 к значению valuesPtr: valuesPtr += 1;

Это вполне допустимое выражение в Objective-C, его можно использовать для указателей на любой тип данных.

В общем случае, если а – массив элементов типа х, рх – указатель на х, i и п – целые константы, то в выражении рх = а;

рх указывает на первый элемент а, и выражение *{рх + i)

дает доступ к значению, содержащемуся в a[i]. Кроме того, в выражении рх += п;

рх указывает на п следующих элементов в массиве, независимо от их типа данных.

Предположим, что fractsPtr указывает на дробь (fraction), содержащуюся в массиве дробей. Ее нужно сложить с дробью, содержащейся в следующем элс– менте этого массива, и прис воить результат объекту result класса Fraction. Для этого можно написать: result = [*fractsPtr add: *(fractsPtr +1)];

Операторы приращения (++) и уменьшения (—) особенно удобны при работе с указателями. Применение к указателю оператора (++) дает такой же результат, как прибавление к указателю 1, а оператор уменьшения (—) действует также, как вычитание 1 из указателя. Таким образом, если textPtr определен как указатель на тип char и указывает на начало массива элементов типа char с именем text, то в результате оператора ++textPtr;

textPtr будет указывать на следующий си мвол в массиве text, то есть на text[1 ]. Аналогичным образом, в результате оператора –textPtr;

textPtr будет указывать на предыдущий символ в массиве text (если, конечно, textPtr не указывал на начало массива text перед выполнением этого оператора).

В Objective-C вполне допустимо сравнение двух переменных-указателей. Это особенно полезно при сравнении двух указателей на элементы одного массива. Так можно убедиться, что указатель valuesPtr не указывает дальше конца массива, содержащего 100 элементов. Для этого нужно сравнить этот указатель с ука-зателем на последний элемент массива. Например, выражение valuesPtr > &values[99]

будет иметь значение TRUE (ненулевое значение), если valuesPtr указывает даль-ше последнего элемента массива values, и будет иметь значение FALSE (нулевое значение) в противном случае. В соответствии с предыдущим описанием мы можем заменить это выражение на его эквивалент valuesPtr > values + 99

values без индекса – это указатель на начало массива values. (Эквивалентно &values[0].)

В программе 13.12 показано использование указателей на массивы. Функция arraySum вычисляет сумму целых элементов, содержащихся в массиве. // Функция для вычисления суммы элементов, содержащихся в массиве целого типа #import int arraySum (int array[], int n) { int sum = 0, *ptr; int *arrayEnd = array + n; for ( ptr = array; ptr < array End; ++ptr) sum += *ptr; return (sum); } int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int arraySum (int array[], int n); int values[10] = { 3, 7, -9, 3,6, -1, 7, 9, 1, -5 }; NSLog (@"The sum is %i", arraySum(values, 10)); [pool drain]; return 0; }

Вывод программы 13.12 The sum is 21 (Сумма равна 21)

Внутри функции arraySum определен указатель arrayEnd на тип int, который указывает «ячейку» непосредственно вслед за последним элементом массива. За-тем для последовательного перебора элементов массива используется цикл for. При входе в цикл ptr указывает на начало массива. На каждом шаге цикла к сумме прибавляется элемент массива, на который указывает ptr. Затем в цикле for значение ptr наращивается, чтобы указывать на следующий элемент массива. Когда ptr указывает «ячейку» после конца массива, происходит выход из цикла for, и значение sum возвращается вызывающей процедуре. Это массив или это указатель?

Чтобы передать массив функции, нужно просто передать имя этого массива, как при вызове функции arraySum. Но мы говорили, что для создания указателя на массив достаточно задать имя этого массива. Из этого следует, что при вызове функции arraySum функции передается указатель на массив values. Именно это и происходит, поэтому мы можем изменять элементы массива внутри функции.

Но если функции перелается указатель на массив, то почему формальный параметр внутри функции не объявлен как указатель? Иначе говоря, почему при объявлении array в функции arraySum не используется следующее объявление: int *array;

Не следует ли все обращения к массиву вну три функции выполнять с помо-щью переменных-указателей?

Чтобы ответить на эти вопросы, мы должны сначала вернуться к тому, что уже говорили об указателях и массивах. Если valuesPtr указывает на тип элемента, содержащегося в массиве с именем values, то выражение (valuesPtr + i) эквива-лентно выражению valuesfi] в предположении, что valuesPtr сначала указывал на начало массива values. Из этого следует, что мы можем использовать выражение (values + i) для обращения к i-му элементу массива values. В общем случае, если х – массив любого типа, то выражение x[i] всегда можно записать в эквивалентной форме *{х + i).

Как видите, указатели и массивы тесно связаны друг с другом, и поэтому внутри функции arraySum можно объявить массив как «массив целых значений (типа int)» или как «указатель на тип int».

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

Чаще всего указатель на массив используется как указатель на символьную стро-ку. Чтобы показать, насколько просты в работе указатели на символьные строки, напишем функцию с именем copyString для копирования одной строки в другую. Используя обычные методы написания такой функции с помощью индексирования массивов, можно составить следующий код: void copyString (char to[], char from[]) { int i; for (i = 0; fromp] != ''; ++i) to[i] = from[i]; to[i] = ''; }

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

Если написать функцию copyString с использованием указателей, то индексная переменная i не нужна. Версия с указателями показана в программе 13.13. #import < Foundation/Foundation.h> void copyString (char *to, char *from) { for (; *from != ''; ++from, ++to ) *to = *from; *to = ''; } int main (int arge, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; void copyString (char *to, char *from); char string [] = "Копируемая строка"2; char string2[50]; copyString (string2, string 1); NSLog(@"%s", string2); copyString (string2, "Строка-константа"); NSLog(@"%s", string2); [pool drain]; return 0; }

Вывод программы 13.13 Копируемая строка Строка-константа

В функции copyString определены два формальных параметра (to и from) как указатели на символы, а не на массивы символов, как в предыдущей версии copyString. Это показывает, каким образом эти две переменные будут использо-ваться в функции.

Затем происходит вход в цикл for (без начальных условий) для копирования строки, которую указывает параметр from (откуда) в строку, которую указывает параметр to (куда). На каждом шаге цикла выполняется увеличение указателей from и to на I. В результате указатель from указывает на следующий символ для копирования из исходной строки, и указатель to указывает место, в котором будет сохранен этот символ. Когда указатель from указывает на нуль-символ, происходит выход из цикла for. Затем функция помещает нуль-символ в конец скопированной строки.

В процедуре main функция copyString вызывается дважды. В первый раз в строку string2 копируется содержимое строки stringl, во второй раз в строку string2 копируется содержимое константной символьной строки. Константные символьные строки и указатели

Из copyString (string2, "Строка-константа");

следует, что если константная символьная строка передается как аргумент фун-кции, эта символьная строка фактически передается указателю. Это верно не только в данном случае. Обобщая, можно сказать, что если константная сим-вольная строка используется в Objective-C, то на эту символьную строку создается указатель.

Это может вызвать некоторую путаницу, но, как мы отмечали в главе 4, кон-стантные символьные строки называются строками встиле С (С-строками). Они не яатяются объектами. Объект константной символьной строки создается в том случае, если перед строкой поставлен знак например, @"This is okay", и мы не можем использовать один вид вместо другого.

Таким образом, если переменная textPtr объявлена как указатель на символ, char *textPtr;

оператор textPtr = "Символьная строка";

присваивает переменной textPtr указатель на константную символьную строку "Символьная строка". Будьте внимательны, чтобы отличать указатели на символь-ные строки от символьных массивов, поскольку приведенное выше присваивание нс подходит для символьного массива. Например, если определить text как массив символов с помощью следующего оператора char text[80];

то мы не сможем написать такой оператор, как text = "Это неправильно";

Единственный случай, когда Objective-C допускает такой тип присваивания для символьного массива —инициализация массива: char text[80] = "Это правильно";

При такой инициализации массива text не происходит создание указателя на символьную строку "Это правильно" внутри text. Вместо этого происходит запись самих символов с конечным нуль-символом в соответствующие элементы массива text.

Если text – это указатель на тип char, то в случае инициализации text с помо-щью оператора char *text = "Это правильно";

ему будет присвоен указатель на символьную строку "Это правильно". Снова об операторах приращения (++) и уменьшения (–)

До настоящего момента оператор приращения или уменьшения был единствен-ным оператором в выражении. Если мы пишем выражение ++х, это означает, что значение х увеличивается на I. И, как вы уже видели, если х – указатель на массив, эта операция делает х указателем на следующий элемент массива.

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

Операторы приращения (increment) и уменьшения (decrement) всегда по-мещаются перед соответствующими наращиваемыми и уменьшаемыми пере-менными. Например, для приращения переменной i достаточно написать: ++i;

Оператор приращения можно также поместить после переменной: i++;

Оба выражения допустимы, и оба дают одинаковый результат: увеличение на I значения i. В первом случае, когда ++ помещается перед операндом, операция увеличения называется преувеличением (pre-increment). Во втором случае, когда ++ помещается после операнда, операция увеличения называется посту-величением.

То же самое относится к оператору уменьшения. Поэтому оператор –i;

выполняет предуменыиение i, а оператор i–;

выполняет постуменьшение i. Оба дают одинаковый результат: вычитание 1 из значения i.

Если операторы приращения и уменьшения используются в более сложных выражениях, то отличия между пред– и пост-операторами могут быть существен-ными.

Предположим, что у нас есть две целые переменные: i и j. Если присвоить i значение 0 и затем написать оператор j = ++i;

то переменной j будет присвоено значение 1, а не 0. В случае оператора преду-величения переменная наращивается до того, как ее значение используется в выражении. В приведенном выше выражении значение i сначала увеличивается с 0 до 1, а затем ее значение присваивается переменной j, как в следующих двух операторах. ++i; i = i;

Если использовать оператор постувеличения j = i++;

значение i будут увеличено после того, как она будет присвоена переменной j. Если i было присвоено значение 0 перед выполнением оператора постувеличс– ния, то переменной j будет присвоено значение 0, и затем значение i будет уве-личено на 1, как в следующих операторах. j = i; ++i;

Еше один пример. Если i равна 1, то в результате оператора X = a[–i];

переменной х будет присвоено значение а[0], так как переменная i уменьшается до того, как ее значение используется для индексации а. Оператор x = a[i–];

присваивает х значение а[1], поскольку i уменьшается после того, как ее значение используется для индексации а.

Третий пример отличий между пред– и пост-операторами приращения и уменьшения. При вызове функции NSLog (@"%i", ++i);

происходит увеличение i, после чего ее значение передается функции NSLog, а при вызове NSLog (@"%i", i++);

увеличение i происходит после того, как ее значение передано этой функции. Таким образом, если значение i было равно 100, при первом вызове NSLog на терминал будет выведено 101, а при втором вызове NSLog – 100. В обоих случаях значение i будет равно 101 после выполнения оператора.

И последний пример. Если textPtr – указатель на тип char, то в выражении *(++textPtr)

textPtr сначала увеличивается на 1, а затем извлекается символ, на который ука-зывает textPtr. Однако в выражении *(textPtr++)

сначала извлекается символ, на который указывает textPtr, а затем увеличивается его значение. В обоих случаях круглые скобки не обязательны, поскольку опера-торы * и ++ имеют одинаковый приоритет, но применяются справа налево.

Вернемся к функции copyString из программы 13.13 и перепишем ее для включения операций прирашения непосредственно в оператор присваивания.

Поскольку указатели to и from наращиваются каждый раз после выполнения оператора присваивания внутри цикла for, их следует включить в оператор при-сваивания как операции пост-увеличения. Модифицированный цикл for из про-граммы 13.13 принимает вид for (; *from != '';) *to++ = *from++;

Выполнение оператора присваивания внутри никла происходит следующим образом. Считывается символ, на который указывает from, затем происходит наращивание from, чтобы он указывал следующий символ в исходной строке. Соответствующий символ будет сохранен в позиции, на которую указывает 1о; затем происходит наращивание to так, чтобы он указывал следующую позицию в конечной строке.

Приведенный цикл не слишком пригоден, поскольку не имеет начального выражения и не содержит выражения цикла. Для этой логики больше подходит цикл while. Он показан в программе 13.14, где представлена новая версия функции copyStriog. В этом цикле while учитывается, что нуль-символ равен значению 0. // Функция для копирования одной строки в другую // с помощью указателей, версия 2 #import void copyString (char *to, char *from) { while (*from) *to++ = *from++; *to = “"; } 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.


    Ваша оценка произведения:

Популярные книги за неделю