Программирование >>  Процедурные приложения 

1 ... 50 51 52 [ 53 ] 54 55 56 ... 150


Функция strlen(), объявленная в файле STRING.H, в качестве аргумента принимает указатель на строку, заканчивающуюся нулевым символом, и возвращает число символов в строке, не считая последнего. В нашем примере строка состоит из семи символов, но счетчик цикла for инициализируется значением 6, поскольку строка в нем интерпретируется как массив, содержащий элементы от нулевого до шестого. На первый взгляд, может показаться странной взаимосвязь между строковыми указателями и массивами символов, но если мы вспомним, что имя массива по сути своей является указателем на первый элемент массива, то станет понятным, почему переход от имени указателя к имени массива в программе не вызвал возражений компилятора.

Ограничения на использование оператора &

Оператор взятия адреса (&) можно применять далеко не с каждым выражением. Ниже иллюстрируются ситуации, когда оператор & используется неправильно:

/*с константами*/ pivariable = &4 8;

/*в в1ражениях с арифметическими операторами*/

int iresult = 5;

pivariable = &(iresult + 15);

/* с переменными класса памяти register*/

register int registerl; pivariable = &registerl;

В первом случае делается недопустимая попытка получить адрес константного значения. Поскольку с константой 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 ( ) (его назначение состоит в чтении из входного потока заданного количества символов) будет выполнен успешно, поскольку в функцию передается действительный адрес массива.



1 ... 50 51 52 [ 53 ] 54 55 56 ... 150

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика