Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

MM2 TP

.pdf
Скачиваний:
9
Добавлен:
09.02.2015
Размер:
3.63 Mб
Скачать

Tакая двусмысленность особенно пагубна в ситуациях типа

IF (N > 0)

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

IF (S[I] > 0) {

PRINTF("...");

RETURN(I);

}

ELSE

PRINTF("Ошибка - N равно нулю\N");

Запись ELSE под IF ясно показывает, что требуется от программы, но компилятор не получит соответствующего указания и свяжет ELSE с внутренним IF. Ошибки такого рода очень трудно обнаруживаются.

3.3ELSE – IF

Возврат_к_оглавлению

Конструкция

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

оператор

ELSE IF (выражение)

оператор

ELSE IF (выражение)

оператор

ELSE

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

довательность операторов IF является наиболее распространенным способом программи-

рования выбора из нескольких возможных вариантов. ВЫРАЖЕНИЯ просматриваются последовательно, если какое-то ВЫРАЖЕНИЕ оказывается истинным, то выполняется относящийся к нему ОПЕРАТОР, и этим вся цепочка заканчивается. Каждый ОПЕРАТОР может быть либо отдельным оператором, после которого ставится точка с запятой, либо группой операторов в фигурных скобках (точка с запятой не ставится).

Последняя часть с ELSE выполняется тогда, когда ни одно из проверяемых условий не выполняется. Довольно часто эта част используется для контроля, чтобы засечь "невоз-

можное" условие.

Для иллюстрации выбора из трех возможных вариантов рассмотрим C – функцию, ко-

торая методом половинного деления определяет, находится ли данное значение X в отсор-

тированном массиве V. Элементы массива V должны быть расположены в порядке воз-

растания. Функция возвращает номер позиции (число между 0 и N-1), в которой значение

X находится в V, и -1, если X не содержится в V.

/* EX0015.C */ int main() {

int v[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; printf("%d", binary(-1, v, 10)); return(0);

}

int binary(x, v, n) int x, v[], n;

{

int low, high, mid;

low = 0; high = n - 1;

while (low <= high) {

mid = (low + high) / 2; if (x < v[mid])

high = mid - 1; else if (x > v[mid])

low = mid + 1;

else

return(mid);

}

return(-1);

}

Основной частью каждого шага алгоритма является проверка, будет ли X меньше,

больше или равен среднему элементу V[MID]; использование конструкции ELSE - IF

здесь вполне естественно.

3.4Переключатель SWITCH

Возврат_к_оглавлению

Оператор SWITCH дает специальный способ выбора одного из многих вариантов, ко-

торый заключается в проверке совпадения значения данного выражения с одной из задан-

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

лов, использующую последовательность IF...ELSE IF...ELSE (EX0011.C). Вот та же самая программа, но с переключателем.

/* EX0016.C */ int main() {

int c, i, nwhite, nother, ndigit[10];

nwhite = nother = 0; for (i = 0; i < 10; i++)

ndigit[i] = 0;

while ((c = getchar()) != eof) switch (c) {

case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8':

case '9': ndigit[c-'0']++; break;

case ' ': case '\n': case '\t':

nwhite++;

break; default :

nother++;

break;

}

printf("digits =");

for (i = 0; i < 10; i++)

printf(" %d", ndigit[i]);

printf("\nwhite space = %d, other = %d\n", nwhite, nother); return(0);

}

Переключатель вычисляет целое выражение в круглых скобках (в данной программе -

значение символа C) и сравнивает его значение со всеми случаями (CASE). Каждый слу-

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

фикса CASE, совпадает со значением целого выражения, то выполнение начинается с это-

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

DEFAULT. Префикс DEFAULT является необязательным ,если его нет, и ни один из слу-

чаев не подходит, то вообще никакие действия не выполняются. Случаи и выбор по умол-

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

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

полнения операторов, соответствующих одному случаю, происходит выполнение опера-

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

BREAK можно использовать и для немедленного выхода из операторов цикла WHILE, FOR и DO.

Переход от одного CASE к следующему ("проваливание") имеет как свои достоинства,

так и недостатки. К положительным качествам можно отнести то, что такой переход по-

зволяет связать несколько случаев с одним действием, как было с пробелом, табуляцией и новой строкой в примере EX0016.C. В то же время "проваливание" приводит к необходи-

мости заканчивать каждый CASE оператором BREAK, чтобы избежать перехода к сле-

дующему. "Проваливание" с одного CASE на другой довольно часто бывает причиной возникновения ошибок при модификации программы.

Хорошим стилем программирования является проставление оператора BREAK после последнего CASE (в примере EX0016.C после DEFAULT), даже если это не является ло-

гически необходимым. При добавлении в конец переключателя еще одного CASE эта ме-

ра предосторожности поможет избавиться от неприятностей.

Упражнение 17.

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

буляции и новой строки на "печатные" последовательности, такие как \N и \T. Сохранять разделение на строки. Использовать переключатель.

3.5Циклы - WHILE и FOR

Возврат_к_оглавлению

В конструкции

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

оператор вычисляется выражение. Если его значение отлично от нуля, то выполняется оператор

и ВЫРАЖЕНИЕ вычисляется снова. Этот цикл продолжается до тех пор, пока значение ВЫРАЖЕНИЯ не станет нулем, после чего выполнение программы продолжается с места после ОПЕРАТОРА. Правило простановки точек с запятой сохраняются такими же, как для предыдущих конструкций.

Конструкция с оператором FOR:

FOR (выражение-1; выражение-2; выражение-3)

оператор

эквивалентен следующей конструкции с оператором WHILE:

выражение-1;

WHILE (выражение-2) {

оператор выражение-3;

}

Грамматически все три компонента в FOR являются выражениями. Наиболее распро-

страненным является случай, когда ВЫРАЖЕНИЕ-1 и ВЫРАЖЕНИЕ-3 являются при-

сваиваниями или обращениями к функциям, а ВЫРАЖЕНИЕ-2 - условным выражением.

Любая из трех частей может быть опущена, хотя точки с запятой при этом должны оста-

ваться. При отсутствии ВЫРАЖЕНИЯ 1 или ВЫРАЖЕНИЯ-3, то ничего необыкновенно-

го не происходит. При отсутствии проверки, т.е. ВЫРАЖЕНИЯ-2, считается, что она дает положительный результат. Например конструкция:

FOR (;;) {

(любые операторы)

}

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

гими средствами (такими как BREAK).

Использование WHILE или FOR - это, дело вкуса. Например в

WHILE ((C = GETCHAR()) == ' ' || C == '\N' || C == '\T')

;

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

тественным.

Цикл FOR, предпочтительнее там, где имеется простая инициализация и реинициали-

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

ются вместе в начале цикла. Это наиболее очевидно в конструкции

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

которая является идиомой языка C для обработки первых N элементов массива. Сле-

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

щая переменная (I) сохраняет свое значение после выхода из цикла, какова бы ни была причина этого выхода. Поскольку компонентами FOR могут быть произвольные выраже-

ния, они не ограничиваются только арифметическими прогрессиями. Тем не менее явля-

ется плохим стилем включать в FOR вычисления, которые не относятся к управлению циклом, лучше поместить их в управляемые циклом операторы.

В качестве примера приведем вариант функции ATOI, преобразующей строку в ее численный эквивалент. Этот вариант допускает присутствие в начале строки символов пустых промежутков и знака + или -.

Общая схема программы отражает форму поступающих данных:

-пропустить пустой промежуток, если он имеется

-извлечь знак, если он имеется

-извлечь целую часть и преобразовать ее

Каждый шаг выполняет свою часть работы и оставляет все в подготовленном состоя-

нии для следующей части. Весь процесс заканчивается на первом символе, который не может быть частью числа. Следующая программа (EX0017.C) имеет следующие особен-

ности:

- оператор i++ возвращает значение переменной i, а затем увеличиваем значение этой переменной на 1. Для сравнения: оператор ++i сначала увеличивает значение переменной i, а затем возвращает увеличенное значение.

- знак ? используется в условном операторе:

выражение-1 ? выражение-2 : выражение-3

Приведенная выше конструкция возвращает значение ВЫРАЖЕНИЯ-2, если значение логического ВЫРАЖЕНИЯ-1 истинно (не равно 0). В противном случае возвращается значение ВЫРАЖЕНИЯ-3.

Итак исходный модуль, содержащий функцию ATOI() и головную MAIN() имеет сле-

дующий вид:

/* Преобразование текста в число и умножение этого числа на 2

Максимальное число, которое правильно обрабатывается 32767

Для увеличения диапазона чисел необходимо:

описать ATOI как DOUBLE;

N как DOUBLE;

вставить прототип DOUBLE ATOI(CHAR S[]);*/ #include <stdio.h>

int main() { int i = 0, c;

char a[100];

while((c = getchar()) != '\n') a[i++] = c;

a[i] = '\0'; printf("%d",atoi(a)*2); return(0);

}

int atoi(s) char s[];

{

int i, sign; int n;

for(i=0;s[i] == ' ' || s[i] == '\n' || s[i] == '\t';i++)

; /* пропуск пустоты. тело цикла - пусто */ sign = 1;

/*после левых пустых символов ожидается символ знака*/ if(s[i] == '+' || s[i] == '-') /* знак */

sign = (s[i++] == '+') ? 1 : - 1; for( n = 0; s[i] >= '0' && s[i] <= '9'; i++)

n = 10 * n + s[i] - '0'; return(sign * n);

}

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

когда имеется несколько вложенных циклов. Следующий пример (EX0018.C) является примером функции, которая сортирует массив целых чисел по методу Шелла. Основная идея сортировки по Шеллу заключается в том, что сначала сравниваются удаленные эле-

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

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

/* EX0018.C */

void shell(int a[], int n); int main() {

int a[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; shell(a, 10);

printf("%d %d %d %d %d %d %d %d %d %d ", a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]);

return(0);

}

void shell(v, n) int v[], n;

{

int gap, i, j, temp;

for (gap = n/2; gap > 0; gap = gap / 2) for (i = gap; i < n; i++)

for (j=i-gap; j>=0 && v[j]>v[j+gap]; j-=gap) { temp = v[j];

v[j] = v[j+gap]; v[j+gap] = temp;

}

}

Здесь имеются три вложенных цикла. Самый внешний цикл управляет интервалом между сравниваемыми элементами, уменьшая его от N/2 вдвое при каждом проходе, пока он не станет равным нулю. Средний цикл сравнивает каждую пару элементов, разделен-

ных на величину интервала; самый внутренний цикл переставляет любую неупорядочен-

ную пару. Так как интервал в конце концов сводится к единице, все элементы в результате упорядочиваются правильно. Отметим, что в силу общности конструкции FOR внешний

цикл укладывается в ту же самую форму, что и остальные, хотя он и не является арифме-

тической прогрессией.

В циклах FOR часто используется операция ЗАПЯТАЯ "," Два выражения, разделен-

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

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

сов.

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

Операция ЗАПЯТАЯ иллюстрируется функцией REVERSE, которая располагает стро-

ку S в обратном порядке.

/* EX0019.C */

void reverse(char s[]); int main() {

char s[] = "raimonda"; reverse(s); printf("%s", s); return(0);

}

void reverse(s) char s[];

{

int c, i, j;

for(i = 0, j = strlen(s) - 1; i < j; i++, j--) { c = s[i];

s[i] = s[j]; s[j] = c;

}

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]