Программирование >>  Унарные и бинарные операторы 

1 ... 10 11 12 [ 13 ] 14 15 16 ... 37


1 04 Глава 5. Функции, указатели и ссылки

Указатель на функцию 105

Чтобы убедиться в том, что программа работает правильно, можно записать в какую-нибудь ячейку массива отрицательное число и посмотреть, что будет выведено на экран.

Указатель на функцию

Слова указатель на функцию звучат дико, мы ведь привыкли к тому, что указывать можно только на объекты, такие как числа, строки и массивы.

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

Теперь подумаем, как объявить этот указатель. Очевидно, больше всего функция похожа на массив, поэтому и указатель на нее выбран таким:

int (*f)Cint.int):

Здесь f - указатель на функцию, принимающую два целочисленных параметра и возвращающую целое число.

Указатель на функцию, как и любой другой, инициализируется оператором &. В программе из листинга 5.10 объявлена функция sum, принимающая две целочисленные переменные и возвращающая их сумму. Кроме того, объявлен указатель Г на аналогичную функцию. Как только f получает адрес начала sum, открывается партизанская тропа к этой функции. Оператор * превращает, как обычно, ука.затель на функцию в саму функцию, следовательно, (*f) (2.3) вернет пятерку, потому что *f - это как бы второе имя sum. Заметим, что скобки вокруг *f

нужны, ведь int *f С int. int) - это совсем не указатель на функцию, а функция, возвращающая указатель.

Листинг 5.10

#inc1ude <iostream> using namespace std; 1nt sum(int a. int b){ return(a+b);

int main{){

int {*f)(int.int):

cout sum{2.3) endl:

f=&sum:

cout (*f){2.3) endl;

return 0;

Обращение к функции через указатель {*f)(2.3) весьма похоже на обра1цение к переменным, но давайте вспомним о сходстве функций и массивов. Если m - массив переменных типа char, а pch - указатель на char, то присвоить указателю адрес нулевого элемента массива можно двумя способами: pch=&m[0] и просто pch=m. То есть имя массива - это еще и адрес его начала. Проделать такое можно потому, что с именем массива не связано значение какого-то его элемента. Если ср - переменная трша char, а pch - указатель па char, то нельзя записать pch=ch, потому что слева и справа от знака равенства стоят объекты разной породы . Но если m - имя массива, состоящего из переменных char, наши руки развязаны, и можно записать pch=m, ничего не нарушая.

То, что справедливо для массивов, должно выполняться и для функций. Ведь имя функции так же свободно , как имя массива. Поэтому имя функции - и есть адрес ее начала, а это значит, что инициализация указателя на функцию не требует оператора &. Очевидно, ана-логию с массивами нужно довести до логического конца. Если адрес нулевого элемента массива используется наравне с его именем, то такой же привилегией должен



обладать и ука.затель па функцию. И.мснно это и подтверждает листинг 5.11, где указатель на функцию и се на-стояп1ее имя оказываются равноправными.

Листинг 5.11

include <iostream> using namespace std; int sumCint a, int b){ return(a+b);

int main{){

int C*f)Cint.int):

cout sum{2.3) endl:

f=sum:

cout f(2.3) endl:

return 0;

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

Чтобы у читателя не сложилось впечатление об указателе на функцию, как о чем-то любопытном, по бесполезном, попробуем !юписать про1рамму, реализуюп1ую разные способы сортировки строк. До сих пор для этого использовалась функция sortO, принимаюи1ая только два отератора - s.beginO и s.endO, указывающие на начало и конец контейнера (см. раздел Файлы в главе 4). При этом строки размс1цалнсь в лекснкоп)афическом порядке, грубо говоря, но алфавиту. Но сун1ествует множество других способов сравнения строк. Например, большей можно считать ту строку, что длиннее, поэтому существует другая функция sortO - уже с тремя параметрами. Первые два - уже знакомые нам итераторы, а третий - указатель на ()ункцию, выполняюн1ую сравнение строк. Эта функция принимает два сравниваемых объекта (в наиюм случае они имеют тип string) и возвращает булеву пе1эеменную - результат сравнения. Еслп

Эта программа сначала создает контейнерный тин vector, содержащий шесть объектов типа string, а затем сортирует его двумя разными способами, подставляя в функцию sort() указа Tejni на разные функции. В первом случае (функция спр) оператор < сравнивает строки лексикографически. Во втором (функция cmpl) сравниваются размеры строк, для чего используются собственные функции sizeO объектов string. Естественно,

объекты сортируются по нозрасганию , то функция должна возвратить true, когда первый обтлкт больше второго, и false - в протпв1Юм случае (листинг5.12).

Листинг 5.12

#include <iostream>

#include <vector>

#include <algorithn>

using namespace std:

bool cmpcstring xl. string x2) {

return xl < x2:

bool cmpl(string xl. string x2) { return xl.sizeO < x2.s1ze(): }

int main(){ vector<string> s: s.pushbackCqapb ): s.push back( царевич ); s.push back( король ): 5.ри5Ь Ьаск( королевич ): s.pusl back( caпoжник ); s.push back( портной ): sort(s.begin().s.end().cmp): for(int i=0:is.size():1++)

cout s[i] endl: cout endl:

sort(s.begi nC).s.end().cmpl): for(int i=0:i<s.size():i++)

cout s[i] endl: return 0:



Ссылки

Так сдружились они, Браконьер и Бобер (Свет не видел примера такого!). Что никто и нигде никогда с этих пор Одного ие встречал без другого.

Льюис Кэрролл, *Охота на Снарка

На первый взгляд ссылки ничем не напоминают указатели. Гораздо больше они похожи на псевдонимы переменных, которые можно испол1>зовать наравне с подлинными именами.

Ссылка объявляется с помощью оператора &, например:

int &b = а:

Здесь а - исходная целочисленная переменная, b - ссылка на нее. В отличие от указателя, ссылка не может существовать сама по себе. Только что объявленная ссылка намертво связывается с переменной, и разлучить их невозможно.

Программа, показанная в листинге 5.13, сначала объявляет переменную а и присваивает ей начальное значение - двойку. Затем объявляется b - ссылка на переменную а. Делается это с помощью оператора &, причем его положение между i nt и b не имеет значения. Следующие объявления эквивалентны:

int&b = а; int& b = а: int &b = а;

Листинг 5.13

#inc1ude <iostream> using namespace std: int main(){ int a=2;

int &b=a:

cout b= b endl: b=2 b-5:

cout a= a endl: a=5 return 0:

В нашей программе ссылке не присваивается никакого начального значения, да в этом и нет нужды, потому что переменная b - это та же переменная а, но иначе называемая. Поэтому сразу после объявления переменная b равна двум. Но если присвоить b иное значение, например 5, то и переменная а гакжс станет равной пяти.

Связь ссылки и самой переменпой можно использовать при передаче аргументов функции. Если персда-ет!:;я ссылка, то ее изменение должно отражаться на самой переменной. Правда, для этого нужно явно ука.зать функции, что она принимает именно ссылку. Программа, показанная в листинге 5.14, делает то же, что программа из листинга 5.5, - меняет содержимое переменных а и Ь. Но на этот раз функция exchngC) принимает не указатели, а ссылки на переменные.

Листинг 5.14

#inc1ude <iostream>

using namespace std:

void exchngCint Sa. int &b){

int tmp=a:

a=b:

b=tmp:

int main(){ int x=2.y=5: exchng(x.y):

cout x= X endl: cout y- у end!: return 0: }

О tom, что функция получает ссылки, а не указатели, говорит заголовок функции void exchngC i nt &a. int &b),

результаты сортировки оказываются разными, и будет поучительно па них посмотреть.



1 ... 10 11 12 [ 13 ] 14 15 16 ... 37

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