Смекни!
smekni.com

начает непосредственный переход к выполнению проверочной

части; в цикле FOR управление передается на шаг реинициали-

зации. (Оператор CONTINUE применяется только в циклах, но не

в переключателях. Оператор CONTINUE внутри переключателя

внутри цикла вызывает выполнение следующей итерации цикла).

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

вает только положительные элементы массива а; отрицательные

значения пропускаются.

FOR (I = 0; I < N; I++) {

IF (A[I] < 0) /* SKIP NEGATIVE ELEMENTS */

CONTINUE;

... /* DO POSITIVE ELEMENTS */

}

Оператор CONTINUE часто используется, когда последующая

часть цикла оказывается слишком сложной, так что рассмотре-

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

бокому уровню вложенности программы.

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

Напишите программу копирования ввода на вывод, с тем ис-

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

строк выводится только одна. (Это простой вариант утилиты

UNIQ систем UNIX).

3.9. Оператор GOTO и метки

В языке “C” предусмотрен и оператор GOTO, которым беско-

нечно злоупотребляют, и метки для ветвления. С формальной

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

и на практике почти всегда можно обойтись без него. Мы не

использовали GOTO в этой книге.

Тем не менее, мы укажем несколько ситуаций, где оператор

GOTO может найти свое место. Наиболее характерным является

его использование тогда, когда нужно прервать выполнение в

некоторой глубоко вложенной структуре, например, выйти сразу

из двух циклов. Здесь нельзя непосредственно использовать

оператор BRеак, так как он прерывает только самый внутренний

цикл. Поэтому:

· 72 -

FOR ( ... )

FOR ( ... ) {

...

IF (DISASTER)

GOTO ERROR;

}

...

ERROR:

CLEAN UP THE MESS

Если программа обработки ошибок нетривиальна и ошибки могут

возникать в нескольких местах, то такая организация оказыва-

ется удобной. Метка имеет такую же форму, что и имя перемен-

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

приписана к любому оператору той же функции, в которой нахо-

дится оператор GOTO.

В качестве другого примера рассмотрим задачу нахождения

первого отрицательного элемента в двумерном массиве. (Много-

мерные массивы рассматриваются в главе 5). Вот одна из воз-

можностей:

FOR (I = 0; I < N; I++)

FOR (J = 0; J < M; J++)

IF (V[I][J] < 0)

GOTO FOUND;

/* DIDN'T FIND */

...

FOUND:

/* FOUND ONE AT POSITION I, J */

...

Программа, использующая оператор GOTO, всегда может быть

написана без него, хотя, возможно, за счет повторения неко-

торых проверок и введения дополнительных переменных. Напри-

мер, программа поиска в массиве примет вид:

FOUND = 0;

FOR (I = 0; I < N && !FOUND; I++)

FOR (J = 0; J < M && !FOUND; J++)

FOUND = V[I][J] < 0;

IF (FOUND)

/* IT WAS AT I-1, J-1 */

...

ELSE

/* NOT FOUND */

...

Хотя мы не являемся в этом вопросе догматиками, нам все

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

весьма умеренно.

· 73 -

4. Функции и структура программ.

Функции разбивают большие вычислительные задачи на ма-

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

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

та. Соответствующие функции часто могут скрывать в себе де-

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

которые нет необходимости, проясняя тем самым всю программу,

как целое, и облегчая мучения при внесении изменений.

Язык “C” разрабатывался со стремлением сделать функции

эффективными и удобными для использования; “C”-программы

обычно состоят из большого числа маленьких функций, а не из

нескольких больших. Программа может размещаться в одном или

нескольких исходных файлах любым удобным образом; исходные

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

наряду со скомпилированными ранее функциями из библиотек. Мы

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

они зависят от используемой системы.

Большинство программистов хорошо знакомы с “библиотечны-

ми” функциями для ввода и вывода /GETCHAR , PUTCHAR/ и для

численных расчетов /SIN, COS, SQRT/. В этой главе мы сообщим

больше о написании новых функций.

4.1. Основные сведения.

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

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

бинацию символов. /Это - специальный случай утилиты GREP

системы “UNIX”/. Например, при поиске комбинации “THE” в на-

боре строк

NOW IS THE TIME

FOR ALL GOOD

MEN TO COME TO THE AID

OF THEIR PARTY

в качестве выхода получим

NOW IS THE TIME

MEN TO COME TO THE AID

OF THEIR PARTY

основная схема выполнения задания четко разделяется на три

части:

WHILE (имеется еще строка)

IF (строка содержит нужную комбинацию)

вывод этой строки


· 74 -

Конечно, возможно запрограммировать все действия в виде

одной основной процедуры, но лучше использовать естественную

структуру задачи и представить каждую часть в виде отдельной

функции. С тремя маленькими кусками легче иметь дело, чем с

одним большим, потому что отдельные не относящиеся к сущест-

ву дела детали можно включить в функции и уменьшить возмож-

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

могут оказаться полезными сами по себе.

“Пока имеется еще строка” - это GETLINE, функция, кото-

рую мы запрограммировали в главе 1, а “вывод этой строки” -

это функция PRINTF, которую уже кто-то подготовил для нас.

Это значит, что нам осталось только написать процедуру для

определения, содержит ли строка данную комбинацию символов

или нет. Мы можем решить эту проблему, позаимствовав разра-

ботку из PL/1: функция INDEX(S,т) возвращает позицию, или

индекс, строки S, где начинается строка T, и -1, если S не

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

не 1, потому что в языке “C” массивы начинаются с позиции

нуль. Когда нам в дальнейшем понадобится проверять на совпа-

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

ко функцию INDEX; остальная часть программы останется той же

самой.

После того, как мы потратили столько усилий на разработ-

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

ний. ниже приводится целиком вся программа, так что вы може-

те видеть, как соединяются вместе отдельные части. Комбина-

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

в качестве символьной строки в аргументе функции INDEX, что

не является самым общим механизмом. Мы скоро вернемся к об-

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

главе 5 покажем, как сделать комбинацию символов параметром,

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

Программа также содержит новый вариант функции GETLINE; вам

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

1.

#DEFINE MAXLINE 1000

MAIN() /* FIND ALL LINES MATCHING A PATTERN */

{

CHAR LINE[MAXLINE];

WHILE (GETLINE(LINE, MAXLINE) > 0)

IF (INDEX(LINE, “THE”) >= 0)

PRINTF(“%S”, LINE);

}

· 75 -

GETLINE(S, LIM) /* GET LINE INTO S, RETURN LENGTH *

CHAR S[];

INT LIM;

{

INT C, I;

I = 0;

WHILE(--LIM>0 && (C=GETCHAR()) != EOF && C != '&bsol;N')

S[I++] = C;

IF (C == '&bsol;N')

S[I++] = C;

S[I] = '&bsol;0';

RETURN(I);

}

INDEX(S,T) /* RETURN INDEX OF T IN S,-1 IF NONE */

CHAR S[], T[];

{

INT I, J, K;

FOR (I = 0; S[I] != '&bsol;0'; I++) {

FOR(J=I, K=0; T[K] !='&bsol;0' && S[J] == T[K]; J++; K++)

;

IF (T[K] == '&bsol;0')

RETURN(I);

}

RETURN(-1);

}

Каждая функция имеет вид имя (список аргументов, если они

имеются) описания аргументов, если они имеются

{

описания и операторы , если они имеются

}

Как и указывается, некоторые части могут отсутство-

вать; минимальной функцией является

DUMMY () { }

которая не совершает никаких действий.

/Такая ничего не делающая функция иногда оказывается

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

раммы/. если функция возвращает что-либо отличное от целого

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

этот вопрос обсуждается в следующем разделе.

· 76 -

Программой является просто набор определений отдельных

функций. Связь между функциями осуществляется через аргумен-

ты и возвращаемые функциями значения /в этом случае/; ее

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

могут располагаться в исходном файле в любом порядке, а сама

исходная программа может размещаться на нескольких файлах,

но так, чтобы ни одна функция не расщеплялась.

Оператор RETURN служит механизмом для возвращения зна-

чения из вызванной функции в функцию, которая к ней обрати-

лась. За RETURN может следовать любое выражение:

RETURN (выражение)

Вызывающая функция может игнорировать возвращаемое

значение, если она этого пожелает. Более того, после RETURN

может не быть вообще никакого выражения; в этом случае в вы-

зывающую программу не передается никакого значения. Управле-

ние также возвращется в вызывающую программу без передачи

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

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

правой фигурной скобки. EСли функция возвращает значение из

одного места и не возвращает никакого значения из другого

места, это не является незаконным, но может быть признаком

каких-то неприятностей. В любом случае “значением” функции,

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

ладочная программа LINT проверяет такие ошибки.

Механика компиляции и загрузки “C”-программ, располо-

женных в нескольких исходных файлах, меняется от системы к

системе. В системе “UNIX”, например, эту работу выполняет

команда 'CC', упомянутая в главе 1. Предположим, что три

функции находятся в трех различных файлах с именами MAIN.с,

GETLINE.C и INDEX.с . Тогда команда

CC MAIN.C GETLINE.C INDEX.C

компилирует эти три файла, помещает полученный настраиваемый объектный код в файлы MAIN.O, GETLINE.O и INDEX.O и загружа-ет их всех в выполняемый файл, называемый A.OUT .