Назад | Начало урока | Вперед
Содержание

Глава 11

Ключевые слова — ключ к диалогу с компьютером


В этой главе...

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

Однако, как вы уже могли убедиться на собственном опыте, течение жизни не всегда прямолинейно и порой случаются отклонения от общего курса. Хорошие программы обладают подобной гибкостью. Существуют, конечно, задачи, которые могут быть решены с помощью последовательно выполняющихся программ. Но в большинстве случаев программы должны уметь реагировать на некие внешние воздействия или команды и, в зависимости от этого, както изменять свое поведение. Для моделирования таких ситуаций C++ предлагает целый набор операторов, позволяющих управлять последовательностью выполняемых действий. Эти операторы разрешают выполнение определенной операции только в случае выполнения указанного условия. Кроме того, они способны выполнять один и тот же набор инструкций до тех пор, пока заданное условие не перестанет выполняться.

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

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

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

Вверх

Великолепная тройка: ключевые слова if, for и while

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

Вверх

Условный оператор

Синтаксис условного оператора if очень прост:

if (expr1)
stmt;

(Обратите внимание, что здесь и далее в книге словом ехрг будет обозначаться любое выражение,
например такое, как i < 1, а словом stmt— инструкция, например cost = cost + 1.)

Вместо expr1 можно подставить любое выражение, которому может быть сопоставлено
логическое значение. Если выражение истинно, выполняется инструкция stmt. Можно использовать фигурные скобки, для того чтобы заключить в них набор инструкций, которые должны будут выполняться в случае, если проверяемое выражение истинно. Например, следующим кодом проверяется значение логической переменной fDcDo, и, если оно совпадает со значением true , выполняется набор из трех инструкций:

if (fDcDo)
{

nDeedle = 0;
nDidle = 1;
nDum = 0;
}

А этот код моделирует ситуацию, в которой покупателю предоставляется скидка, если он сделал покупки на сумму, превышающую $3 000:

if (nCount > 3000)
{

dblDiscount = .2;
}

Можно придать оператору if большую гибкость, если использовать вместе с ним ключевое слово else :

if (expr1)

stmt1;
else
stmt 2;

В этом случае, если проверяемое выражение (expr1) ложно, выполняется инструкция stmt 2.

Приведенный ниже код моделирует эпизод карточной игры, когда проверяются набранные игроком очки, и, если их сумма превышает число 21, фиксируется перебор (fBusted), в противном случае предлагается взять еше одну карту:

if (nHandValue > 21)
{

//В картах перебор
nUserScore -= nBet;
fBusted = true;
}
else
{

//Игроку предлагается взять еще одну карту
cout << "Еще?\n";
cin >> fHitMe;

}

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

if (nHandValue > 21)
{

//В картах перебор
nUserScore -= nBet;
fBusted = true;
}
else
{
//Игроку предлагается взять еще одну карту
cout « "Еще?\п";
cin » fHitMe;
}

А вот тот же код, набранный без отступов:

if (nHandValue > 21)(
//В картах перебор
nUserScore -= nBet; fBusted = true;} else {

" //Игроку предлагается взять еще одну карту
cout « "Еще?\п"; cin >> fHitMe;
}

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

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

Например:

if (too)
{

bar-f+
if (bar > 3)
{
baz = 2;
}
if (goober < 7)
{
flibber - 3;
}
}

Другим приемом, облегчающим чтение кодов программы (который также использовался во всех приведенных выше примерах), является размещение закрывающей фигурной скобки } на одном уровне с соответствующей ей открывающей { скобкой:

if (fFoo)
{

}

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

if (fFoo)
nVal++;
nCheck++;

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

if (fFoo)
{

nVai++;
nCheck++;
}

Visuaf C++ .NET форматирует коды вашей программы автоматически. Например, если вы выделите с помощью отступа какую-то строку, набираемые после нее строки будут автоматически выравниваться по : ней. Если вы наберете скобку }, она автоматически будет выровнена по соответствующей ей скобке { .

Следующий код определяет размер предоставляемой скидки в зависимости от суммы со вершенных покупок:

//Если сумма превышает $5000, скидка равна 30%
if (nSumrr, > 5000)
{

dblDiscount = 0.3 ;
}
else
{
//Если сумма превышает $3000, скидка равна 20%
if (nSumm > 3000)
{
dblDiscount = 0.2;
}
//В противном случае скидка не предоставляется
else
{
dblDiscount = 0.0;
}
}

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

Вверх

Оператор for

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

for(exp1; expr2; ехргЗ) stmt1;

Такой код еше называют циклом for. При выполнении цикла for в первую очередь обрабатывается выражение expr1, которым определяется начальное значение переменной, используемой для контроля за количеством итераций. (Итерация — это одноразовое выполнение инструкций цикла.) Затем оценивается выражение ехрг2. (Оно оценивается перед каждой итерацией, для того чтобы определить, следует ли продолжать выполнение цикла.) Если выражение ехрг2 истинно, выполняется инструкция strati, после чего обрабатывается выражение ехргЗ. Выражение ехргЗ используется обычно для изменения значения переменной, контролирующей количество итераций. Если выражение ехрг2 ложно, выполнение цикла прекращается и программа переходит к обработке следующих за этим циклом кодов.

Пример использования цикла for

Приведенное выше объяснение может показаться вам не очень понятным, поэтому про- демонстрируем работу цикла for на практическом примере.

int i;
for (i = 0; i < 2; i++)
{

Console::WriteLine(i.ToString());
}

Опишем, как этот код выполняется.

  1. Вначале обрабатывается выражение ехрг1. Переменной i присваивается значение 0.

  2. Затем обрабатывается выражение ехрг2. Проверяется, меньше ли значение переменной i числа 2. Поскольку переменной i только что было присвоено значение 0, выражение ехрг2 будет истинным, что дает разрешение на выполнение инструкции stmt1. В данном случае эта инструкция выглядит так:

    Console::WriteLine(i.ToString()) ;

    Ее выполнение приводит к отображению на экране значения переменной i.

  3. Далее обрабатывается выражение ехргЗ. В данном случае оно выглядит как i++, таким образом, значение переменной i увеличивается на единицу.

  4. Поскольку выражение ехрг2 должно оцениваться перед выполнением каждой итерации, программа снова возвращается к нему. Переменная i имеет уже значение 1, но это все равно меньше числа 2. Поэтому инструкция stmt1 выполняется еще раз, и на экране отображается следующее значение переменной i.

  5. Снова обрабатывается выражение ехргЗ. Значение переменной i увеличивается на
    единицу и становиться равным числу 2.

  6. Программа переходит к проверке выражения ехрг2. Теперь оно ложно, поскольку значение переменной i (2) не меньше числа 2. На этом выполнение цикла заканчивается.

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

Повторение ради повторения Если вы хотите, чтобы инструкция была выполнена несколько раз подряд, наберите такой код:

for (i = 0; i < n; i++)
{

//Инструкция или набор инструкций для повторения
}

Значение n определяет количество выполняемых итераций. Например, если вам нужно,
чтобы строка "Терпение и еще раз терпение" была выведена на экран 50 раз подряд, наберите такой код;

for (i = 0; i < 50; i++)
{

Console: :WriteLine (S''Tepn-ение и еще раз терпение");
}

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

int nCount;
Console::WriteLine(S"Сколько строк отобразить?");
nCount = Console::ReadLine()->ToInt32();
for (i = 0; i < nCount; i++)
{

Console: :WriteLine (S"Терпение и еще раз терпение") ;
}

Вычисление факториала

Наверное, каждый программист когда-нибудь сталкивался с такой проблемой: как написать код, вычисляющий n-факториал?
Это выражение (n-факториал) вычисляется по такой формуле: nх (n - 1) х (n - 2) х ... х1.
Так, 2-факториал (пишется как 2! в математических книгах, но не в компьютерных кодах) вычисляется как 2 х 1, а 3! - как 3 х 2 х 1.
Одним из вариантов решения такой проблемы может быть приведенный ниже код.

//Вычисление n!
n = Int32 :: Parse (Console : : ReadLine () ) ;

if (n == 1)
{

Console::WriteLine(S"l" ) ;
}
else if (n == 2)
{
Console::WriteLine(S"2" ) ;
}
else if (n == 3)
{
Console :: WriteLine ( S " 6 " ) ;
}

Этот способ, несмотря на свою простоту, крайне неэффективен, поскольку, даже если вы будете продолжать аналогичным образом набирать коды для чисел 4, 5, ... 35 и т.д., все равно вы не сможете перебрать даже малую часть всех возможных вариантов. Реальным решением этой проблемы может быть использование цикла for .

// Factorial
// Computes n!

#include "stdafx.h"
#using <mscorlib.dll>
using namespace System;

// This is the entry point for this application
#ifdef _UNICODE
int wmain(void)
#else
int main(void)
#endif
{

int nNumber; //The number the user types in
int nResult = 1;
int i; //Loop variable

//Get the value
Console::WriteLine("What is the number?");
nNumber = Int32::Parse(Console::ReadLine());

//Now loop through. Each time through the loop
//multiply the result by i. This will give
//1*2*3...n because i starts at 1 and increases
//until it is n
for (i=1; i<=nNumber; i++)
{

nResult *= i;
}

//Print the result
Console::WriteLine(S"n! is {0}", nResult.ToString());

//Hang out until the user is finished
Console::WriteLine(L"Hit the enter key to stop the program");
Console::ReadLine();

return 0;

}

Вверх

Оператор while

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

while [exprl]
stmt;

Вначале оценивается выражение exprl. Если оно истинно, выполняется инструкция
stmt. Затем опять проверяется выражение exprl. До тех пор пока выражение exprl будет оставаться истинным, инструкция stmt будет выполняться снова и снова. Как только выражение exprl становится ложным, выполнение цикла прекращается.
Например, если нужно создать цикл, выполняющий десять итераций, наберите такой код:

int i = 0;
while (i < 10]

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

Бесконечный цикл

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

//Компьютер завис
int main(void)
{

while(1);
}

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

Вверх

Ключевые слова switch u do

Visual C++ имеет в своем запасе еще пару операторов, управляющих последовательно
стью выполняемых инструкций. Познакомимся с ними поближе.

Оператор switch

Оператор switch подобен оператору if, но позволяет учесть сразу множество вари-
антов и выбрать только один из них. (Описание каждого варианта начинается с ключевого слова case.) Таким образом, если необходимо создать код, решающий задачу наподобие; "если это Вася, тогда ..., если это Маша, тогда ..., если это Дима, тогда ...", используйте оператор switch, который может заменить собой сразу несколько операторов if. Синтаксис оператора switch выглядит так:

switch (ехрr)
{

case vall:
stmtl;

case val2:
stint 2;

default:
dflt stint;

}

Вначале оценивается выражение ехрг и его значение сравнивается со значением vall.(Значение vall должно быть каким-нибудь числом, например 1 или 23,5.) Если значение выражения ехрг совпадает со значением vall , выполняется инструкция stmtl и все последующие за ней инструкции цикла switch. Если значение выражения ехрг не совпадает со значением vall , оно сравнивается со значением val2 и т.д.

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

Если вы знакомы с языком Visual Basic, вы, наверное, ожидаете, что, как и в
Visual Basic, в качестве проверяемых значений val могут быть использованы
строки. К сожалению, это не так. Сравниваться могут только числовые значения,
но не текстовые.

Продемонстрируем работу оператора switch на примере, отображающем названия некоторых чисел:
//Названия чисел 1, 2, 3 и 4
switch (n)
{

case 1:
Console::WriteLine(3"один");
break;

case 2 :
Console::WriteLineIS"два");
break;

case 3:
Console::WriteLine(три");
break;

case 4:
Console::WriteLine(S"четыре");
brеак;

default:
Console::WriteLine(S"неизвестное число");

}

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

Результат
1 одиндватричетыренеизвестное число
2 дватричетыренеизвестное число
и т.д.

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

Оператор do

Выполнение цикла do во многом подобно выполнению цикла while. Отличие состоит в
том, что цикл while проверяет истинность выражения до того, как будет выполнена какая-либо инструкция. Поэтому возможен вариант (если выражение сразу окажется ложным), при котором ни одна из инструкций не будет выполнена. Цикл do, наоборот, вначале выполняет первую итерацию и только затем проверяет истинность выражения. Если выражение истинно, выполняется следующая итерация и т.д. Когда выражение становится ложным, выполнение цикла прекращается.

do
stint;
while (expr);

Вот пример использование цикла do, который выполняется до тех пор, пока значение переменной i не сравняется со значением переменной n:

int i = 0;
do
Console::WriteLine(i.ToString());
while (i < n);

Если даже переменная n имеет значение 0, это число все равно будет отображено на экране, поскольку проверяемое выражение (в данном случае i

Выражение i = i + 1 эквивалентно выражению i++.


Назад | Начало урока | Вверх | Вперед
Содержание