Смекни!
smekni.com

что как A[5][5], так и B[5][5] являются законными ссылками

на отдельное число типа INT. Но A - настоящий массив: под

него отводится 100 ячеек памяти и для нахождения любого ука-

занного элемента проводятся обычные вычисления с прямоуголь-

ными индексами. Для B, однако, описание выделяет только 10

указателей; каждый указатель должен быть установлен так,

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

каждый из них указывает на массив из 10 элементов, то тогда

где-то будет отведено 100 ячеек памяти плюс еще десять ячеек

для указателей. Таким образом, массив указателей использует

несколько больший объем памяти и может требовать наличие яв-

ного шага инициализации. Но при этом возникают два преиму-

щества: доступ к элементу осуществляется косвенно через ука-

затель, а не посредством умножения и сложения, и строки мас-

сива могут иметь различные длины. Это означает, что каждый

элемент B не должен обязательно указывать на вектор из 10

элементов; некоторые могут указывать на вектор из двух эле-

ментов, другие - из двадцати, а третьи могут вообще ни на

что не указывать.

Хотя мы вели это обсуждение в терминах целых, несомнен-

но, чаще всего массивы указателей используются так, как мы

продемонстрировали на функции MONTH_NAME, - для хранения

символьных строк различной длины.

Упражнение 5-6.

Перепишите функции DAY_OF_YEAR и MONTH_DAY, используя

вместо индексации указатели.

·
120 -

5.11. Командная строка аргументов

Системные средства, на которые опирается реализация язы-

ка “с”, позволяют передавать командную строку аргументов или

параметров начинающей выполняться программе. Когда функция

MAIN вызывается к исполнению, она вызывается с двумя аргу-

ментами. Первый аргумент (условно называемый ARGC) указывает

число аргументов в командной строке, с которыми происходит

обращение к программе; второй аргумент (ARGV) является ука-

зателем на массив символьных строк, содержащих эти аргумен-

ты, по одному в строке. Работа с такими строками - это обыч-

ное использование многоуровневых указателей.

Самую простую иллюстрацию этой возможности и необходимых

при этом описаний дает программа ECHO, которая просто печа-

тает в одну строку аргументы командной строки, разделяя их

пробелами. Таким образом, если дана команда

ECHO HELLO, WORLD

то выходом будет

HELLO, WORLD

по соглашению ARGV[0] является именем, по которому вызывает-

ся программа, так что ARGC по меньшей мере равен 1. В приве-

денном выше примере ARGC равен 3, а ARGV[0], ARGV[1] и

ARGV[2] равны соответственно “ECHO”, “HELLO,” и “WORLD”.

Первым фактическим агументом является ARGV[1], а последним -

ARGV[ARGC-1]. Если ARGC равен 1, то за именем программы не

следует никакой командной строки аргументов. Все это показа-

но в ECHO:

MAIN(ARGC, ARGV) /* ECHO ARGUMENTS; 1ST VERSION */

INT ARGC;

CHAR *ARGV[];

\(

INT I;

FOR (I = 1; I < ARGC; I++)

PRINTF(“%S%C”, ARGV[I], (I<ARGC-1) ? ' ' : '&bsol;N');

&bsol;)

Поскольку ARGV является указателем на массив указателей, то

существует несколько способов написания этой программы, ис-

пользующих работу с указателем, а не с индексацией массива.

Мы продемонстрируем два варианта.

MAIN(ARGC, ARGV) /* ECHO ARGUMENTS; 2ND VERSION */

INT ARGC;

CHAR *ARGV[];

&bsol;(

WHILE (--ARGC > 0)

PRINTF(“%S%C”,*++ARGV, (ARGC > 1) ? ' ' : '&bsol;N');

&bsol;)

· 121 -

Так как ARGV является указателем на начало массива строк-ар-

гументов, то, увеличив его на 1 (++ARGV), мы вынуждаем его

указывать на подлинный аргумент ARGV[1], а не на ARGV[0].

Каждое последующее увеличение передвигает его на следующий

аргумент; при этом *ARGV становится указателем на этот аргу-

мент. одновременно величина ARGC уменьшается; когда она об-

ратится в нуль, все аргументы будут уже напечатаны.

Другой вариант:

MAIN(ARGC, ARGV) /* ECHO ARGUMENTS; 3RD VERSION */ INT ARGC;

CHAR *ARGV[];

&bsol;(

WHILE (--ARGC > 0)

PRINTF((ARGC > 1) ? “%S” : “%S&bsol;N”, *++ARGV);

&bsol;)

Эта версия показывает, что аргумент формата функции PRINTF

может быть выражением, точно так же, как и любой другой. Та-

кое использование встречается не очень часто, но его все же

стоит запомнить.

Как второй пример, давайте внесем некоторые усовершенст-

вования в программу отыскания заданной комбинации символов

из главы 4. Если вы помните, мы поместили искомую комбинацию

глубоко внутрь программы, что очевидно является совершенно

неудовлетворительным. Следуя утилите GREP системы UNIX, да-

вайте изменим программу так, чтобы эта комбинация указыва-

лась в качестве первого аргумента строки.

#DEFINE MAXLINE 1000

MAIN(ARGC, ARGV) /* FIND PATTERN FROM FIRST ARGUMENT */ INT ARGC;

CHAR *ARGV[];

&bsol;(

CHAR LINE[MAXLINE];

IF (ARGC != 2)

PRINTF (“USAGE: FIND PATTERN&bsol;N”);

ELSE

WHILE (GETLINE(LINE, MAXLINE) > 0)

IF (INDEX(LINE, ARGV[1] >= 0)

PRINTF(“%S”, LINE);

&bsol;)

Теперь может быть развита основная модель, иллюстрирую-

щая дальнейшее использование указателей. Предположим, что

нам надо предусмотреть два необязательных аргумента. Один

утверждает: “напечатать все строки за исключением тех, кото-

рые содержат данную комбинацию”, второй гласит: “перед каж-

дой выводимой строкой должен печататься ее номер”.

· 122 -

Общепринятым соглашением в “с”-программах является то,

что аргумент, начинающийся со знака минус, вводит необяза-

тельный признак или параметр. Если мы, для того, чтобы сооб-

щить об инверсии, выберем -X, а для указания о нумерации

нужных строк выберем -N(“номер”), то команда

FIND -X -N THE

при входных данных

NOW IS THE TIME

FOR ALL GOOD MEN

TO COME TO THE AID

OF THEIR PARTY.

Должна выдать

2:FOR ALL GOOD MEN

Нужно, чтобы необязательные аргументы могли располагать-

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

не зависела от количества фактически присутствующих аргумен-

тов. в частности, вызов функции INDEX не должен содержать

ссылку на ARGV[2], когда присутствует один необязательный

аргумент, и на ARGV[1], когда его нет. Более того, для поль-

зователей удобно, чтобы необязательные аргументы можно было

объединить в виде:

FIND -NX THE

вот сама программа:

#DEFINE MAXLINE 1000

MAIN(ARGC, ARGV) /* FIND PATTERN FROM FIRST ARGUMENT */ INT ARGC;

CHAR *ARGV[];

&bsol;(

CHAR LINE[MAXLINE], *S;

LONG LINENO = 0;

INT EXCEPT = 0, NUMBER = 0;

WHILE (--ARGC > 0 && (*++ARGV)[0] == '-')

FOR (S = ARGV[0]+1; *S != '&bsol;0'; S++)

SWITCH (*S) &bsol;(

CASE 'X':

EXCEPT = 1;

BREAK;

·
123 -

CASE 'N':

NUMBER = 1;

BREAK;

DEFAULT:

PRINTF(“FIND: ILLEGAL OPTION %C&bsol;N”, *S);

ARGC = 0;

BREAK;

&bsol;)

IF (ARGC != 1)

PRINTF(“USAGE: FIND -X -N PATTERN&bsol;N”);

ELSE

WHILE (GETLINе(LINE, MAXLINE) > 0) &bsol;(

LINENO++;

IF ((INDEX(LINE, *ARGV) >= 0) != EXCEPT) &bsol;

IF (NUMBER)

PRINTF(“%LD: “, LINENO);

PRINTF(“%S”, LINE);

&bsol;)

&bsol;)

&bsol;)

Аргумент ARGV увеличивается перед каждым необязательным

аргументом, в то время как аргумент ARGC уменьшается. если

нет ошибок, то в конце цикла величина ARGC должна равняться

1, а *ARGV должно указывать на заданную комбинацию. Обратите

внимание на то, что *++ARGV является указателем аргументной

строки; (*++ARGV)[0] - ее первый символ. Круглые скобки

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

совершенно отличный (и неправильный) вид *++(ARGV[0]). Дру-

гой правильной формой была бы **++ARGV.

Упражнение 5-7.

Напишите программу ADD, вычисляющую обратное польское

выражение из командной строки. Например,

ADD 2 3 4 + *

вычисляет 2*(3+4).

Упражнение 5-8.

Модифицируйте программы ENTAB и DETAB (указанные в ка-

честве упражнений в главе 1) так, чтобы они получали список

табуляционных остановок в качестве аргументов. Если аргумен-

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

Упражнение 5-9.

Расширьте ENTAB и DETAB таким образом, чтобы они воспри-

нимали сокращенную нотацию

ENTAB M +N

·
124 -

означающую табуляционные остановки через каждые N столбцов,

начиная со столбца M. Выберите удобное (для пользователя)

поведение функции по умолчанию.

Упражнение 5-10.

Напишите программу для функции TAIL, печатающей послед-

ние N строк из своего файла ввода. Пусть по умолчанию N рав-

но 10, но это число может быть изменено с помощью необяза-

тельного аргумента, так что

TAIL -N

печатает последние N строк. программа должна действовать ра-

ционально, какими бы неразумными ни были бы ввод или значе-

ние N. Составьте программу так, чтобы она оптимальным обра-

зом использовала доступную память: строки должны храниться,

как в функции SORT, а не в двумерном массиве фиксированного

размера.

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

В языке “с” сами функции не являются переменными, но

имеется возможность определить указатель на функцию, который

можно обрабатывать, передавать другим функциям, помещать в

массивы и т.д. Мы проиллюстрируем это, проведя модификацию

написанной ранее программы сортировки так, чтобы при задании

необязательного аргумента -N она бы сортировала строки ввода

численно, а не лексикографически.

Сортировка часто состоит из трех частей - сравнения, ко-

торое определяет упорядочивание любой пары объектов, перес-

тановки, изменяющей их порядок, и алгоритма сортировки, осу-

ществляющего сравнения и перестановки до тех пор, пока

объекты не расположатся в нужном порядке. Алгоритм сортиров-

ки не зависит от операций сравнения и перестановки, так что,

передавая в него различные функции сравнения и перестановки,

мы можем организовать сортировку по различным критериям.

Именно такой подход используется в нашей новой программе

сортировки.

Как и прежде, лексикографическое сравнение двух строк

осуществляется функцией STRCMP, а перестановка функцией

SWAP; нам нужна еще функция NUMCMP, сравнивающая две строки

на основе численного значения и возвращающая условное указа-

ние того же вида, что и STRCMP. Эти три функции описываются

в MAIN и указатели на них передаются в SORT. В свою очередь

функция SORT обращается к этим функциям через их указатели.

мы урезали обработку ошибок в аргументах с тем, чтобы сосре-

доточиться на главных вопросах.