|
Программирование >> Процедурные приложения
Функция strlen(), объявленная в файле STRING.H, в качестве аргумента принимает указатель на строку, заканчивающуюся нулевым символом, и возвращает число символов в строке, не считая последнего. В нашем примере строка состоит из семи символов, но счетчик цикла for инициализируется значением 6, поскольку строка в нем интерпретируется как массив, содержащий элементы от нулевого до шестого. На первый взгляд, может показаться странной взаимосвязь между строковыми указателями и массивами символов, но если мы вспомним, что имя массива по сути своей является указателем на первый элемент массива, то станет понятным, почему переход от имени указателя к имени массива в программе не вызвал возражений компилятора. Ограничения на использование оператора & Оператор взятия адреса (&) можно применять далеко не с каждым выражением. Ниже иллюстрируются ситуации, когда оператор & используется неправильно: /*с константами*/ pivariable = &4 8; /*в в1ражениях с арифметическими операторами*/ int iresult = 5; pivariable = &(iresult + 15); /* с переменными класса памяти register*/ register int registerl; pivariable = ®isterl; В первом случае делается недопустимая попытка получить адрес константного значения. Поскольку с константой 48 не связана ни одна ячейка памяти, операция не может быть выполнена. Во втором случае программа пытается найти адрес выражения iresult+15. Поскольку результатом этого выражения является число, находящееся в программном стеке, его адрес не может быть получен. В последнем примере объявляется регистровая переменная, смысл которой состоит в том, что предполагается частое ее использование, поэтому она должна располагаться не в памяти, а непосредственно в регистрах процессора. Компилятор может проигнорировать подобный запрос и разместить переменную в памяти, но в любом случае операция взятия адреса считается неприменимой по отношению к регистровым переменным. Указатели на массивы Как уже говорилось, указатели и массивы логически связаны друг с другом. Вспомните из предыдущей главы, что имя массива является константой, содержащей адрес первого элемента массива. В связи с этим значение имени массива не может быть изменено оператором присваивания или каким-нибудь другим оператором. Например, ниже создается массив типа floatс именем ftemperatures: #define IMAXREADINGS 20 float ftemperatures[IMAXREADINGS]; float *pftemp; В следующей строке объявленному выше указателю pftemp присваивается адрес первого элемента массива: pftemp = ftemperatures; Это же выражение можно записать следующим образом: pftemp = &ftemperatures[0]; Тем не менее, даже если указатель описан для хранения адреса переменных типа float, все равно следующие выражения недопустимы: ftemperatures = pftemp; &ftemperatures[0]= pftemp; Эти выражения невыполнимы, поскольку в них делается попытка изменить константу ftemperatures и эквивалентное ей выражение &ftemperatures[0] , что так же бессмысленно, как и строка 10 = pftemp; Указатели на указатели В C/C++ можно создавать указатели на другие указатели, которые, в свою очередь, содержат адреса реальных переменных. Смысл этого процесса проиллюстрирован на рис. 9.11, где ppiявляется указателем на указатель. Рис. 9.11. Пример указателя на указатель Чтобы объявить в программе указатель, который будет хранить адрес другого указателя, нужно просто удвоить число звездочек в объявлении: int **ppi; Каждый символ * читается как указатель на. Таким образом, количество указателей в цепочке, задающее уровень косвенной адресации, соответствует числу звездочек перед именем идентификатора. Уровень косвенной адресации определяет, сколько раз следует выполнить операцию раскрытия указателя, чтобы получить значение конечной переменной. В следующем фрагменте создается ряд указателей с различными уровнями косвенной адресации (рис. 9.12). int ivalue = 10; int *pi; int **ppi; *pppi; pi = &ivalue; ppi = π pppi= &ppi; lvalue Рис. 9.12. Несколько уровней косвенной адресации В первых четырех строках объявляются четыре переменные: ivalue типа int, указатель piна переменную типа int (первый уровень косвенной адресации), указатель ppi (второй уровень косвенной адресации) и указатель pppi (третий уровень косвенной адресации). Этот пример показывает, что при желании можно создать указатель любого уровня. В пятой строке указателю первого уровня piприсваивается адрес переменной ivalue. Теперь значение переменной ivalue(10) может быть получено с помощью выражения *pi. В шестой строке в указатель второго уровня ppiзаписывается адрес (но не содержимое) указателя pi, который, в свою очередь, указывает на переменную ivalue. Обратиться к значению переменной можно будет посредством выражения **ppi. В последней строке аналогичным образом заполняется указатель третьего уровня. В C/C++ указатели можно инициализировать сразу при объявлении, как и любые другие переменные. Например, указатель pppi можно было бы инициализировать следующей строкой: int ***pppi = &ppi; Указатели на строки Строковые константы в действительности представляют собой символьные массивы с конечным нулевым символом (рис. 9.13). Указатель на строку можно объявить и инициализировать следующим образом: char *psz= Файл не найден ; Рис. 9.13. Схема представления в памяти строки символов Данное выражение создает указатель psz типа char и присваивает ему адрес первого символа строки - Ф (рис. 9.14). Сама строка заносится компилятором в специальную служебную таблицу. Рис. 9.14. Инициализация указателя на строку Приведенную выше строку можно записать по-другому: char *psz; psz= Файл не найден ; Следующий пример иллюстрирует одно часто встречающееся заблуждение, касающееся использования строковых указателей и символьных массивов: char *psz= Файл не найден ; char pszаrray[] = Файл не найден ; Основное различие между этими двумя выражениями состоит в том, что значение указателя psz может быть изменено (поскольку указатель является разновидностью переменной), а значение имени массива pszarray не может быть изменено, так как это константа. По этой причине показанные ниже выражения ошибочны: /* Ошибочный код */ char pszarray[15]; pszarray= Файл не найден ; Хотя, на первый взгляд, все кажется логичным, в действительности в данном выражении оператор присваивания пытается скопировать адрес первой ячейки строки Файл не найден в объект pszarray.- Но поскольку имя массива pszarray является константой, а не переменной-указателем, то будет выдано сообщение об ошибке. Следующий фрагмент на языке C++ ошибочен по той причине, что указатель был объявлен, но не инициализирован: /* Ошибочный код */ char *psz; cin >> psz; Чтобы решить проблему, нужно просто зарезервировать память для указателя: char string [10]; char *psz = string; cin.get (psz,10) ; Поскольку имя string представляет собой адрес первой ячейки массива, то второе выражение не только резервирует память для указателя, но и инициализирует его, присваивая ему адрес первого элемента массива string. В данном случае метод cin.get ( ) (его назначение состоит в чтении из входного потока заданного количества символов) будет выполнен успешно, поскольку в функцию передается действительный адрес массива.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |