Опубликован: 05.07.2006 | Уровень: для всех | Доступ: платный
Лекция 6:

Указатели и массивы

5.12. Указатели на функции

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

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

Как и прежде, лексикографическое сравнение двух строк осуществляется функцией strcmp, а перестановка функцией swap ; нам нужна еще функция numcmp, сравнивающая две строки на основе численного значения и возвращающая условное указание того же вида, что и strcmp. Эти три функции описываются в main и указатели на них передаются в sort. В свою очередь функция sort обращается к этим функциям через их указатели. мы урезали обработку ошибок в аргументах с тем, чтобы сосредоточиться на главных вопросах.

#define lines 100 /* max number of lines
       to be sorted */

main(argc, argv) /* sort input lines */
int argc;
char *argv[];
{
 char *lineptr[lines]; /* pointers to text lines */
 int nlines; /* number of input lines read */
 int strcmp(), numcmp(); /* comparsion functions */
 int swap(); /* exchange function */
 int numeric = 0; /* 1 if numeric sort */

 if(argc>1 && argv[1][0] == '-' && argv[1][1]=='n')
    numeric = 1;
 if(nlines = readlines(lineptr, lines)) >= 0) {
    if (numeric)
          sort(lineptr, nlines, numcmp, swap);
    else
          sort(lineptr, nlines, strcmp, swap);
    writelines(lineptr, nlines);
 } else
    printf("input too big to sort\n");
}

Здесь strcmp, numcmp и swap - адреса функций ; так как известно, что это функции, операция & здесь не нужна совершенно аналогично тому, как она не нужна и перед именем массива. Передача адресов функций организуется компилятором.

Второй шаг состоит в модификации sort:

sort(v, n, comp, exch) /* sort strings v[0] ... v[n-1] */
char *v[];           /* into increasing order */
int n;
int (*comp)(), (*exch)();
{
 int gap, i, j;

 for(gap = n/2; gap > 0; gap /= 2)
     for(i = gap; i < n; i++)
    for(j = i-gap; j >= 0; j -= gap) {
        if((*comp)(v[j], v[j+gap]) <= 0)
            break;
        (*exch)(&v[j], &v[j+gap]);
    }
}

Здесь следует обратить определенное внимание на описания. описание

int (*comp)()

говорит, что comp является указателем на функцию, которая возвращает значение типа int. Первые круглые скобки здесь необходимы; без них описание

int *comp()

говорило бы, что comp является функцией, возвращающей указатель на целые, что, конечно, совершенно другая вещь.

Использование comp в строке

if ((*comp)(v[j], v[j+gap]) <= 0)

полностью согласуется с описанием: comp - указатель на функцию, *comp - сама функция, а

(*comp)(v[j], v[j+gap])

- обращение к ней. круглые скобки необходимы для правильного объединения компонентов.

Мы уже приводили функцию strcmp, сравнивающую две строки по первому численному значению:

numcmp(s1, s2) /* compare s1 and s2 numerically */
char *s1, *s2;
{
 double atof(), v1, v2;

 v1 = atof(s1);
 v2 = atof(s2);
 if(v1 < v2)
    return(-1);
 else if(v1 > v2)
    return(1);
 else
    return (0);
}

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

swap(px, py) /* interchange *px and *py */
char *px[], *py[];
{
 char *temp;

 temp = *px;
 *px = *py;
 *py = temp;
}

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

Упражнение 5-11

Модифицируйте sort таким образом, чтобы она работала с меткой -r, указывающей на сортировку в обратном (убывающем) порядке. Конечно, -r должна работать с -n.

Упражнение 5-12

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

Упражнение 5-13

Добавьте необязательный аргумент -d ("словарное упорядочивание"), при наличии которого сравниваются только буквы, числа и пробелы. Позаботьтесь о том, чтобы эта функция работала и вместе с -f.

Упражнение 5-14

Добавьте возможность обработки полей, так чтобы можно было сортировать поля внутри строк. Каждое поле должно сортироваться в соответствии с независимым набором необязательных аргументов. (предметный указатель этой книги сортировался с помощью аргументов -df для категории указателя и с -n для номеров страниц).

Ярослав Воробей
Ярослав Воробей
Россия
Дмитрий Левин
Дмитрий Левин
Россия