Рис. 9.5. Объявление и использование указателей
Например, целые числа можно умножать, но делать это в отношении указателей не допускается. Таким образом, указатель в действительности представляет собой новый тип, а не целочисленный. По этой причине, как упоминалось ранее, в ANSI С специально для указателей предусмотрена форма %р.
Использование указателей для обмена данными между функциями
Мы лишь слегка коснулись многообразного и удивительного мира указателей, но нас интересует применение указателей для решения задачи обмена данными. В листинге 9.15 представлена программа, в которой с помощью указателей обеспечивается работа функции interchange(). Давайте рассмотрим код, запустим ее и выясним, как она рабо тает.
Листинг 9.15. Программа swap3.c

360 Глава 9
Заработает ли программа из листинга 9.15 после компиляции?
Первоначально х = 5 и у = 10.
Теперь х = 10 и у = 5.
Да, она работает.
Давайте теперь проанализируем код из листинга 9.15. Прежде всего, вызов функции выглядит так:
interchange (&х, &у);
Вместо передачи значений х и у функция передаются их адреса. Это означает, что формальные аргументы и и v, указанные в прототипе и определении функции interchange(), в качестве своих значений будут содержать адреса. Следовательно, они должны быть объявлены как указатели. Поскольку х и у являются целочисленными, а и и v — указателями на целочисленные значения, они объявляются следующим образом:
void interchange (int * u, int * v)
Далее в теле функции содержится объявление, которое предоставляет область памяти, необходимую для временного хранения:
int temp;
Для сохранения значения х в temp используется оператор
temp = *u;
Вспомните, что и имеет значение &х, поэтому и указывает на х. Это означает, что *и дает значение х, что и требовалось. Не следует записывать
temp = и; /* Неправильно */
поскольку в этом случае переменной temp присваивается адрес переменной х вместо ее значения, а задача состоит в том, чтобы осуществить обмен значениями, но не адресами.
Аналогично, чтобы присвоить переменной х значение переменной у, применяйте оператор
* u = * v;
который, в конечном счете, дает следующий результат:
X = у;
Итак, подведем итоги рассмотренного примера. Пам была нужна функция, которая меняет между собой значения переменных х и у. Передавая в функцию адреса х и у, мы предоставляем interchange() доступ к этим переменным. Используя указатели и операцию *, эта функция может выяснить значения, хранящиеся в этих ячейках, и изменить их.
В прототипе ANSI С имена переменных можно не указывать. Тогда объявление в прототипе будет выглядеть так:
void interchange(int *, int *);
В общем случае в функцию можно передать два вида информации о переменной. Если вызов имеет следующий вид, то в функцию передается значение переменной х:
functionl(х);
Если же вызвать функцию, как показано ниже, в нее передается адрес переменной х:
function2(&х);
Функции 361
Первая форма требует, чтобы определение функции включало формальный аргумент того же типа, что и х:
int functionl (int num)
Во второй форме определение функции должно включать формальный параметр, который является указателем на корректный тип:
int function2(int * ptr)
Применяйте первую форму, если функции необходимо передать значение для какого-то вычисления или действия. Используйте вторую форму, если функция должна изменять значения переменных из вызывающей функции. Все это вы уже делали с функцией scanf(). Когда необходимо прочитать значение для переменной (например, num), вы применяете вызов scanf ("%d", &num). Функция читает значение и затем использует адрес для сохранения значения.
Указатели дают возможность обойти тот факт, что переменные внутри interchange() являются локальными. Они позволяют этой функции изменять то, что хранится в main().
Пользователи, знакомые с языками Pascal и Modula-2, могут заметить, что первая форма аналогична параметру-значению, а вторая форма подобна (но не идентична) параметру-переменной в Pascal. Пользователи C++ узнают переменные указатели и заинтересуются, не имеет ли С подобно языку C++ также и ссылочные переменные. Ответ на этот вопрос отрицателен. Пользователям, работающим с BASIC, все это может показаться несколько нарушающим общий порядок. Если материал данного раздела показался непонятным, будьте уверены, что после небольшой практики применение указателей станет простым, обычным и удобным делом (рис. 9.6).
Читать дальше