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

Язык Си - Уэйт, Прата, Мартин

.pdf
Скачиваний:
583
Добавлен:
01.06.2015
Размер:
4.92 Mб
Скачать

Произносимые личностные моменты относятся и к вам.

ОПЕРАЦИЯ УСЛОВИЯ: ?:

Далее Содержание

В языке Си имеется короткий способ записи одного из видов оператора if-else. Он называется "условным выражением" и использует операцию условия - ?:. Эта операция состоит из двух частей и содержит три операнда. Ниже приводится пример оператора с помощью которого находится абсолютное значение числа:

x = (y < 0 )? -y : y;

Все, что находится между знаком = и символом "точка с занятой" представляет собой условное выражение. Смысл этого оператора заключается в следующем: если у меньше 0, то х = - у; в противном случае х = у. В терминах оператора if-else данный оператор мог выглядеть так:

if(у < 0) x = (y < 0 )? -y : y;

х= -у;

else

х= у;

Вобщем виде условное выражение можно записать следующим образом:

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

Если выражение1 истинно (больше нуля), то значением всего условного выражения является величина выражения2;

если выражение1 ложно (равно 0), то значение всего условного выражения - величина выражения3.

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

mах = (а > b)? а : b;

Вообще говоря, использование условных выражений не являетcя обязательным, поскольку тех же результатов можно достичь при помощи операторов if-else. Однако условные выражения более компактны, и их применение обычно приводит к получению более компактного машинного кода.

Резюме: операция условия

I.Операция условия: ?:

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

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

Значение всего выражения равно величине выражения2, если выражение1 истинно, и величине выражения3 в противном случае.

II. ПРИМЕРЫ:

Выражение: (5 > 3) ? 1 : 2 имеет значение 1 Выражение: (3 > 5) ? 1 : 2 имеет значение 2

Выражение: (а > b) ? a : b имеет значение большей из величин а и b.

МНОЖЕСТВЕННЫЙ ВЫБОР: ОПЕРАТОРЫ switch И break

Далее Содержание

141

Операция условия и конструкция if-else облегчают написание программ, в которых осуществляется выбор между двумя вариантами. Однако иногда в программе необходимо произвести выбор одного из нескольких вариантов. Мы можем сделать это используя конструкцию if-else if - ... - else, но во многих случаях оказывается более удобным использовать оператор

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

/*животные*/ main( )

{

char ch;

printf("Введите букву алфавита, а я укажу"); printf("название животного, \n начинающееся с нее.\n");

printf("Введите, пожалуйста, букву; для завершения работы введите #. \n"); while((ch = getchar())!= '#')

{

if(ch != '\n') /* пропуск символа "новая строка" */

{

if(ch >= 'a' && ch <= 'я') /*разрешены только строчные буквы */ switch (ch)

{ case 'a' : printf(" аргали, дикий горный азиатский баран\n"); break;

case 'б' : printf(" бабирусса, дикая малайская свинья \n"); break;

case 'в' : printf(" выхухоль, водоплавающий крот \n"); break;

case 'г' : printf(" гиббон, длиннорукая обезьяна \n"); break;

case 'д' : printf(" даман древесный \n"); break;

default: printf(" Это трудная задача!\n"); break;

} else

printf(" Я распознаю только строчные буквы. \n"); printf(" Введите, пожалуйста, следующую букву или #.\n");

}/* конец if, пропускающего символ "новая строка" */

}/* конец цикла while */

}

РИС. 7.7. Программа, печатающая названия животных.

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

Введите букву алфавита, а я укажу название животного, начинающееся с нее.

Введите, пожалуйста, букву; для завершения работы введите #. а [возврат]

аргали, дикий горный азиатский баран Введите, пожалуйста, следующую букву или #. г [возврат] гиббон, длиннорукая обезьяна Введите, пожалуйста, следующую букву или #. р [возврат] Это трудная задача!

Введите, пожалуйста, следующую букву или #. Т [возврат] Я распознаю только строчные буквы. Введите, пожалуйста, следующую букву или #. # [возврат]

Этот пример служит иллюстрацией работы оператора swith. Вначале вычисляется выражение в скобках, расположенное за ключевым словом switch. В данном случае значением этого выражения будет символ, присвоенный переменной ch, который мы ввели перед этим. Затем программа просматривает список "меток" (в этом примере case 'a':, case'б': и т. д.) до тех пор, пока не находит "метку", которая соответствует данному значению. Далее программа переходит к выполнению оператора, расположенного в этой строке. Что произойдет в случае, когда такой подходящей строки не найдется? Если существует строка с "меткой" case default:, то будет выполняться оператор,

142

помеченный этой меткой. В противном случае произойдет переход к оператору, расположенному за оператором switch.

Что можно сказать по поводу оператора break? Его выполнение приводит к тому, что в программе происходит выход из оператора switch и осуществляется переход к следующему за ним оператору (см. рис. 7.8). При отсутствии оператора break будут выполнены все операторы, начиная с помеченного данной меткой и завершая оператором switch. Если удалить все операторы break из нашей программы, то, указав, например, букву г, получим следующий диалог:

Введите букву алфавита, а я укажу название животного, начинающееся с нее. Введите, пожалуйста, букву; для завершения работы введите #.

г [возврат] гиббон, длиннорукая обезьяна даман древесный это трудная задача

Введите, пожалуйста, следующую букву или #.

# [возврат]

Мы видим, что выполнились все операторы, начиная от метки case 'г' и кончая оператором switch. Если вы знакомы с языком Паскаль, то можете заметить, что оператор switch в Си похож на оператор case в Паскале. Важнейшее отличие состоит в том, что если вы хотите, чтобы в каждом конкретном случае выполнялся только помеченный оператор, то в операторе switch необходимо использовать операторы break.

Метки, имеющиеся в операторе switch, должны быть константами или константными

выражениями (выражения, операнды которого константы) целого типа (включая тип char). Запрещается использовать в качестве метки переменную. Значением выражения в скобках должна

быть величина целого типа (опять же, включая тип char). Ниже приводится общая структура оператора switch:

switch(целоe выражение)

{ case константа1 : операторы; (необязательные) case константа2 : операторы; (необязательные)

case default (необязательные) : операторы; (необязательные)

}

switch (number)

{

case 1: оператор 1; break;

case 2: оператор 2; break;

сазе 3: оператор 3; break; default:оператор 4;

}

оператор5;

switch (number) { case 1: оператор 1; case 2: оператор 2; case 3: оператор 3; defauit: оператор 4;

}

оператор 5;

В обоих случаях значение number равно 2.

РИС. 7.8. Ход выполнения программы, использующей оператор switch при наличии или в

143

отсутствии операторов break

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

case 'E': case 'e':

printf(" ехидна, муравьед колючий \n" ); break;

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

Наша программа имеет две небольшие особенности, о которых мы хотели бы упомянуть. Первая поскольку мы собираемся использовать программу в диалоговом режиме, мы решили

воспользоваться символом # вместо EOF в качестве признака прекращения ее работы. В работе компьютера могли бы возникнуть сложности, если бы он предложил нам ввести признак EOF или даже какой-нибудь управляющий символ, между тем как символ # вполне подходит для этой цели. Поскольку теперь отсутствует необходимость чтения символа EOF, мы не должны описывать в программе переменную ch типа int.

Вторая мы использовали оператор if, который позволяет игнорировать символы "новая строка" при вводе символов в программу. Это тоже некоторая плата за возможность диалоговой работы.

Без этого оператора if каждый раз при нажатии клавиши [возврат] программе пришлось бы рассматривать данный признак как прочитанный символ.

Когда требуется использовать оператор switch, а когда конструкцию else-if? Часто у нас нет возможности выбора. Вы не можете применить оператор switch, когда выбор вариантов основывается на вычислении значения переменной или выражения типа int. Удобного способа

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

if(integer < 1000 && integer > 2)

В то время как замена этой строки оператором switch приведет к необходимости ввести в

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

Резюме: множественный выбор вариантов с помощью оператора switch

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

Как выражения, так и метки должны иметь значения целого типа (включая тип char), метки должны быть константами или константными выражениями. Если не которому значению выражения не соответствует никакая метка, управление передается оператору с меткой default (если такой имеется). Впротивном случае управление передается оператору, следующему за оператором switch.

III. Форма:

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

{ case метка1: оператор1

144

case метка2: оператор2 default: оператор3

}

В операторе может присутствовать более чем 2 помеченных оператора, а наличие метки default является необязательным.

IV. Пример

switch (letter)

{case 'a':

case 'e': printf(" %c - это гласная \n ", letter); case 'c':

case 'n': printf(" Символ %с в наборе букв \ саnе\ n ", letter); default: printf(" Добрый день. \n" );

}

Если переменная letter имеет значение 'a' или 'e', будут выведены на печать все три сообщения, если же 'c' или 'n', то последние два. В случае остальных значений будет напечатано только последнее сообщение.

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

ЧТО ВЫ ДОЛЖНЫ БЫЛИ УЗНАТЬ В ЭТОЙ ГЛАВЕ

Далее Содержание

Как осуществить выбор из двух возможностей выполнить оператор или пропустить его: с помощью оператора if.

Как осуществить выбор одного из двух вариантов: с помощью оператора if-else.

Как осуществить выбор одного из нескольких вариантов: с помощью операторов else-if, switch Операции отношения: > >= = = <= < != Логические операции: && || !

Операция условия: : ?

ВОПРОСЫ И ОТВЕТЫ

Содержание

Вопросы

1.Определите, какие выражения истинны, а какие ложны.

а. 100 > 3 б. 'а' > 'с'

в. 100 > 3 && 'а' > 'с' г. 100 > 3 || 'a' > 'с' д. !(100 > 3)

2.Запишите выражения, соответствующие следующим условиям а. Значение number равно или больше 1, но меньше 9

б. Значение ch не равно q или k

в. Значение number лежит между 1 и 9, но не равно 5 г. Значение number не лежит между 1 и 9

145

3. В программе, приведенной ниже, наряду с неоправданно сложными условными выражениями имеются и прямые ошибки. Уточните эту программу и исправьте в ней ошибки.

main( )

/* 1 */

 

 

{

/* 2 */

 

 

 

 

int

weight, height; /* вес в фунтах, рост в дюймах */

 

 

 

 

/* 4

*/

scanf('' %d, weight, height); /* 5

*/

if(weight < 100)

 

/* 6

*/

 

 

if (height >= 72)

/* 7

*/

 

printf(" Для такого веса у вас слишком большой рост \n");

else if (height <

72 && > 64)

/*

9 */

 

printf(" У вас большой рост для вашего веса. \n" );

else if (weight >

300 && !(weight

< = 300))

/* 11*/

if( !(height >= 48)

/* 12

*/

 

 

printf(" Для такого веса у вас слишком маленький рост.\n" );

else

/*

14

*/

 

printf(" У

вас идеальный вес. \n");

/* 15*/

 

/*

16

*/

 

}

 

 

 

 

Ответы

1.Выражения истинны в вопросах а и г

2.a. number > = 1 && number < 9

б. ch != 'q' && ch != k

Замечание: выражение ch != q || ch!= k всегда будет иметь значение "истина", потому что если

переменная сh равна q, то она не может равняться k, и второе условие оказывается выполненным врезультате все выражения "ИЛИ" будет истинным.

в. number > 1 && number < 9 && number != 5

г. !(number > 1 && number < 9) или number < = 1 || number > = 9

Замечание: сказать, что число НЕ лежит между 1 и 9 это то же самое, что сказать: число равно или меньше 1 ИЛИ равно или больше 9. Вторая форма несколько неуклюже звучит на словах, но проще записывается в виде выражения.

3. Строка 5: должна выглядеть так scanf(" %d %d", &weight, &height). He забывайте указывать

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

Строка 9: подразумеваемое выражение должно выглядеть так: (height < 72 && height > 64). Однако первая часть этого выражения необязательна, поскольку величина height, если поток управления достигнет записи else-if, будет обязательно меньше 72. Поэтому более простое условие (height > 64) в данном случае служит той же цели.

Строка 11: избыточное условие; второе подвыражение (отрицание условия "величина weight меньше или равна 300") означает то же, что и первое. В действительности данное условие записывается так: (weight > 300). Но неприятности на этом не кончаются. Строка 11 относится к ошибочному оператору if. Очевидно, что эта часть else ассоциируется с оператором if, расположенным в строке 6, но, согласно правилу, связывающему ее с ближайшим отрицанием условия, содержащегося в if, она будет ассоциироваться с оператором if на строке 9. Поэтому условие, помещенное на строке 11, будет проверяться в том случае, когда величина weight меньше 100, а величина height меньше или равна 64. Это делает невозможным превышение переменной

weight значения 300 при выполнении данного оператора.

Строки 7-9 должны быть заключены в фигурные скобки. Тогда строка 11 станет альтернативой оператору, расположенному на строке 6, а не на строке 9.

Строка 12: данное выражение необходимо упростить так: (height < 48)

Строка 14: это ключевое слово else относится к последнему оператору if, раcположенному на

146

строке 12. Операторы, помещенные на строках 12 и 13, необходимо заключить в фигурные скобки,

тогда else будет относиться к оператору if на строке 11. Обратите внимание, что последнее сообщение будет напечатано для тех, чей вес заключен между 100 и 300 фунтами.

1)В программу входит слово score - счет (в игре) - Прим. перев.

2)См. перевод на с. 154 - Прим. ред.

3)В нем используется слово legs - ноги - Прим. перев.

4)В оригинале - игра слов complex (англ.) - сложный и комплексный - Прим. перев.

ЦИКЛЫ ВЛОЖЕННЫЕ ЦИКЛЫ

ОСУЩЕСТВЛЕНИЕ ПЕРЕХОДОВ В ПРОГРАММЕ ИСПОЛЬЗОВАНИЕ ЦИКЛОВ ПРИ РАБОТЕ С МАССИВАМИ

КЛЮЧЕВЫЕ СЛОВА

whilе, do, for, break, continue, goto

ОПЕРАЦИИ

+= -= *= /= %=

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

средства реализации таких требований. Мы уже смогли убедиться в чрезвычайной ценности цикла if в том случае, когда необходимо повторить некоторую операцию несколько раз. В языке Си, кроме

того, реализовано еще два вида циклов: цикл for и цикл do ... while. В данной главе рассматриваются принципы работы управляющих структур и даются рекомендации, каким образом

лучше всего применять каждую из них. Обсудим операторы break, continue, goto и операцию "запятая" все они могут использоваться для управления ходом выполнения программы. Кроме того, мы расскажем вам еще немного о свойствах, которые часто используются вместе с циклами.

ЦИКЛ while

Далее Содержание

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

/* угадывание числа1 */ /* неэффективный способ угадывания */ #inlude

main( )

{

int guess = 1; char response;

printf(" Задумайте целое число от 1 до 100. Я попробую угадать");

147

printf(" его.\n Отвечайте д, если моя догадка правильна и"); printf(" \n н, если я ошибаюсь. \n");

рintf("Итак ... ваше число %d?\n" , guess); while((response = getchar( )) != 'д') /* получение ответа*/

if (response != ' \n') /* пропуск символа "новая строка" */ printf(" Ну, тогда оно равно %d?\n", ++guess);

printf(" Я знала, что смогу сделать это!\n");

}

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

случае, если ее догадка неверна, но фактически любой ответ, отличный от символа д, приводит к тому, что программа входит в цикл. Однако если введен символ "новая строка", то при данном прохождении тела цикла никаких операций не производится. Получение другого символа приводит к очередной попытке угадывания целого числа. (Что произошло бы в случае использования операции

guess++ вместо операции ++guess?).

Строка if(response ! = '\n') позволяет программе игнорировать поступление постороннего символа "новая строка", когда вы нажмете клавишу [ввод].

В этом случае тело цикла while не нужно заключать в фигурные скобки, поскольку оператор if, хотя он и занимает две строки в программе, рассматривается как один оператор.

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

В общем виде цикл while записывается так:

while(выражение) оператор

В наших примерах в качестве выражении использовались условные выражения, но, вообще говоря, это могут быть выражения произвольного типа. В качестве оператора можно использовать простой оператор с символом "точка с запятой" в конце или составной oпeратор, заключенный в фигурные скобки. Если выражение истинно (т.е в общем случае не равно нулю), то оператор,

входящий в цикл while выполняется один раз, а затем выражение проверяется снова, а последовательность действий, состоящая из проверки и выполнения оператора, периодически повторяется до тех пор, пока выражение не станет ложным (или в общем случае равным нулю). Такой шаг называется "итерация". Данная структура аналогична структуре оператора if. Основное отличие заключается в том, что в операторе if проверка условия и (возможное) выполнение оператора осуществляется только один раз, а в цикле while эти действия производятся, вообще говоря, неоднократно.

148

РИС. 8.1. Структура цикла while.

Завершение цикла while

Далее Содержание

Мы подошли к самому существенному моменту рассмотрения циклов while. При построении

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

index = 1; while(index < 5)

printf("Доброе утро!\n");

Данный фрагмент программы печатает это радостное сообщение бесконечное число раз,

поскольку в цикле отсутствуют конструкции, изменяющие величину переменной index, которой было присвоено значение 1.

index = 1; while(--index < 5)

printf("Как колеблются старые атомы!\n");

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

величина переменной index станет меньше наименьшего отрицательного числа, допустимого в системе.

Цикл while является "условным" циклом, использующим предусловие (т.е. условие на входе). Он называется условным, потому что выполнение оператора зависит от истинности условия,

описываемого с помощью выражения. Действительно ли значение переменной index меньше 5? Является ли последний введенный символ признаком EOF? Подобное выражение задает предусловие, поскольку выполнение этого условия должно быть проверено перед началом выполнения тела цикла. В ситуации, аналогичной приведенной ниже, тело цикла не выполнится ни разу, потому что используемое условие с самого начала является ложным.

index = 10; while(index++ < 5)

printf(" Желаю хорошо провести день.\n");

Измените первую строку на index = 3;

149

и вы получите работающую программу.

АЛГОРИТМЫ И ПСЕВДОКОД

Далее Содержание

А теперь вернемся к нашей "тупоумной" программе, угадывающей число. Недостаток этой программы кроется не в программировании самом по себе, а в "алгоритме", т.е. методе, используемом для отгадывания числа. Этот метод можно описать следующим образом: попросите пользователя задумать число компьютер начинает угадывание с 1 до тех пор пока догадка неверна, предлагаемое значение увеличивается на 1.

Эта запись, между прочим, служит примером "псевдокода" представляющего собой способ выражения смысла программ на разговорном языке и являющегося некоторым аналогом языка машины. Псевдокод очень эффективен при разработке логики программы. После того как логика покажется вам правильной, вы можете обратить основное внимание на детали перевода псевдокода на реальный язык программирования. Преимущество использования псевдокода состоит в том, что он позволяет сконцентрироваться на логике и структуре программы, не заботясь пока о способе перевода этих идей на язык машины. Если мы хотим улучшить программу, нам в первую очередь необходимо улучшить алгоритм. Один из методов заключается в том, чтобы выбрать число где-нибудь посередине между 1 и 100 (50 нам вполне подходит) и попросить пользователя ответить больше ли это число задуманного, меньше его или равно ему. Если он сообщает, что данное число слишком велико, то тем самым из рассмотрения немедленно исключаются все числа между 50 и 100. Следующей догадкой программы является число, выбранное где-то посередине между 1 и 49. И снова ответ на вопрос, велико или мало это число, позволит исключить из рассмотрения половину оставшихся возможных чисел; программа продолжает указанный процесс, быстро сужая поле поиска до тех пор, пока задуманное число не

будет угадано. Давайте запишем эти логические рассужденя на псевдокоде. Пусть highest - максимально возможная величина отгадываемого числа, a lowest - его минимально возможное значение. Вначале этими величинами будут соответственно 100 и 1, поэтому алгоритм запишется следующим образом:

установить highest равным 100 установить lowest равным 1

попросить пользователя задумать число

предложенное значение (guess) равно (highest + lowest)/2 пока догадка неверна, делать следующее:

{если предложенное значение велико, установить highest равным этому предложенному значению минус 1 если предложенное значение мало, установить lowest равным этому предложенному значению плюс 1

новое предложенное значение равно (highest + lowest)/2 }

Обратите внимание на логику алгоритма: если предложенное значение, равное 50, велико, то максимально возможная величина задуманного числа будет равна 49. Если же значение 50 мало, то минимально возможная величина числа будет равна 51.

Сейчас мы переведем текст, указанный выше, на язык Си. Полученная программа представлена на рис. 8.2.

/* угадывание числа2 */ /* более эффективный способ угадывания*/ #include

#define HIGH 100 #define LOW 1 main( )

{

int guess = (HIGH + LOW)/2; int highest = HIGH;

int lowest = LOW; char response;

150