Как сделать попытки в c

Добавил пользователь Morpheus
Обновлено: 05.10.2024

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

Итак, как обычно начинаем с планирования:

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

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

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

Для генерации случайных чисел в С++ предусмотрена специальная функция – rand(). Что такое функция, мы поговорим на следующем уроке. А сейчас вы просто следуйте предложенным примерам.
Для того, чтобы использовать ее в своих программах необходимо подключить к исходному коду заголовочный файл stdlib.h. Как это делается я надеюсь, вы помните:

Ну и небольшой пример :

using namespace std;

using namespace std;

using namespace std;

void main()
<
srand(time(0)); // инициализация генератора
int max, min, r; // переменные для максимального, минимального и случайного
max=min=rand(); // присвоение случайного значение переменным

for(int i=0;i max) // если r больше имеющегося максимума
max=r; // то оно становится максимумом

результат равен 2. Это остаток от целочисленного деления. То есть 20 = (3*6)+2. Здесь тройка ближайший делитель, а оставшаяся двойка и есть результат.

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

int x=20,y=6,i,res;
i = 20/6;
res = 20-( i * 6 );

Здесь сначала происходит обычное деление 20 на 6. Результат равен 3,3 (3 целых 3 десятых). Поскольку i у нас целочисленная переменная, запомните, что числа вроде 3,3 в ней хранится не смогут, и в результате преобразования типов дробная часть пропадает. Остается только целая часть 3.
Теперь мы множим эту тройку на шесть и то, что получилось, отнимаем от двадцати.
Результат это и будет то, что называют остатком от деления, и это как раз то, что получается при делении по модулю.
Теперь мы знаем, как этот оператор работает, что же нам это дает. А то, что при делении по модулю результат всегда будет меньше делителя как минимум на единицу.
Поэтому поделив таким способом любое случайное число, например, на десять, мы в результате получим число от 0 до 9.
То есть:

не зависимо от того что здесь выдаст rand(), х будет присвоено значение от 0 до 9.

Так, думаю, немного разобрались. Но что делать, если нам необходимо число не от 0 и до, а например от 50 и до 80.

Вот как это вычислить:

int min = 50; // начало диапазона
int max = 80; // конец диапазона
int res = min + (rand()%(max-min));

Сначала вычисляется разность между максимумом и минимумом, что равно 30.
Затем в части выражения rand()%(max-min) получаем случайное число в диапазоне от 0 до 30, и прибавляем его к 50 (минимуму в данном случае), получив таким образом число в необходимом диапазоне.
Надеюсь вам все понятно, ибо на этом с генерацией случайных чисел мы закончили и переходим непосредственно к написанию кода игры.

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

using namespace std;

void main()
<
setlocale(LC_ALL,"Rus");
int r, // случайное число
i=-1, // вводимое значение
c=0; // счетчик колличества попыток

srand(time(0)); // инициализация генератора
r=rand()%100; // генерация случайного числа от 0 до 100

Последняя категория инструкций управления порядком выполнения программы, которую мы рассмотрим, – это остановки. Остановка – это инструкция управления порядком выполнения программы, которая завершает программу. В C++ остановки реализованы как функции (а не как ключевые слова), поэтому наши инструкции остановки будут вызовами функций.

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

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

Затем вызывается специальная функция std::exit() со значением, возвращаемым из main() (код состояния), переданным в качестве аргумента. Так что же такое std::exit() ?

Функция std::exit()

std::exit() выполняет ряд функций очистки. Сначала уничтожаются объекты со статической продолжительностью хранения. Затем выполняется дополнительная очистка файлов, если использовались какие-либо файлы. Наконец, управление возвращается обратно в ОС, а аргумент, переданный в std::exit() , используется в качестве кода состояния.

Явный вызов std::exit()

Хотя std::exit() при завершении функции main() вызывается неявно, она также может быть вызвана явно, чтобы остановить программу до того момента, как она завершится нормально. Когда std::exit() вызывается таким образом, вам нужно будет включить заголовок cstdlib .

Вот пример явного использования std::exit() :

Эта программа печатает:

Обратите внимание, что инструкции после вызова std::exit() никогда не выполняются, потому что программа уже завершена.

Хотя в приведенной выше программе мы вызываем std::exit() из функции main() , std::exit() можно вызвать из любой функции для завершения программы в необходимой точке.

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

Предупреждение

Функция std::exit() не очищает локальные переменные в текущей функции и в функциях выше по стеку вызовов.

std::atexit

Поскольку std::exit() завершает программу немедленно, вы можете перед завершением выполнить какую-либо очистку вручную (в приведенном выше примере мы вручную вызывали функцию cleanup() ). Поэтому программисту необходимо не забывать вручную вызывать функцию очистки перед каждым вызовом exit() .

Чтобы помочь в этом, C++ предлагает функцию std::atexit() , которая позволяет вам указать функцию, которая будет автоматически вызываться при завершении программы через std::exit() .

Эта программа имеет тот же вывод, что и предыдущий пример:

Так зачем вам это делать? Это позволяет вам указать функцию очистки в одном месте (возможно, в main ), а затем не беспокоиться о том, чтобы не забыть вызвать эту функцию явно перед вызовом std::exit() .

Для продвинутых читателей

В многопоточных программах вызов std::exit() может привести к сбою вашей программы (поскольку поток, вызывающий std::exit() , будет очищать статические объекты, к которым могут обращаться другие потоки). По этой причине в C++ появилась еще одна пара функций, которые работают аналогично std::exit() и std::atexit() , – std::quick_exit() и std::at_quick_exit() . std::quick_exit() завершает программу нормально, но не очищает статические объекты и может выполнять или не выполнять другие типы очистки. std::at_quick_exit() выполняет ту же роль, что и std::atexit() для программ, завершаемых с помощью std::quick_exit() .

std::abort и std::terminate

C++ также содержит две другие функции, связанные с остановкой.

Функция std::abort() вызывает аварийное завершение вашей программы. Аварийное завершение означает, что в программе произошла какая-то необычная ошибка времени выполнения, и программа не может продолжать работу. Например, к аварийному завершению попытка разделить на 0 приведет. std::abort() не выполняет никакой очистки.

Случаи, когда std::abort вызывается неявно, мы увидим в этой главе позже (7.17 – assert и static_assert ).

Функция std::terminate() обычно используется вместе с исключениями (мы рассмотрим исключения в следующей главе). Хотя std::terminate можно вызвать явно, она чаще вызывается неявно, когда исключение не обрабатывается (и в некоторых других случаях, связанных с исключениями). По умолчанию std::terminate() вызывает std::abort() .

Когда следует использовать остановки?

Лучшая практика

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

Только маленькие программы удобно хранить в одном файле. Разбиение кода на несколько файлов может упростить разработку и ускорить компиляцию.

Вынос части кода в соседний .cpp файл

Файл можно разделить на несколько частей и положить эти .cpp файлы в одну директорию.

Назовём главный файл, где Вы вызываете main() Main.cpp

Создайте файл Functions.cpp и перенесите туда все функции.

В Main.cpp укажите названия функций, которые вы перенесли и аргументы, которые они принимают.

Создайте новый файл Functions.cpp который будет содержат только функции:

double add( double x, double y)

Header Files

Чтобы не перечислять все функции которые вы хотите использовать в главном файле можно упомянуть их в специальном Header файле с расширением .h

Файл Functions.h

нужны, чтобы один и тот же .h файл не подгружался многократно. После первого подключения все остальные попытки буду проигнорированы.

Файл Functions.cpp остаётся без изменений.

Компиляция и запуск

Чтобы скомпилировать проект нужно перечислить все нужные .cpp файлы.

В данном примере это

g++ main.cpp Functions.cpp -o main
./main

Пример с перегрузкой

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

Если вы разди эксперимента хотите перегрузить функцию add() ничего в описанном выше подходе менять не нужно.

Просто добавьте перегруженную функцию в Functions.cpp и Functions.h а потом вызовите из main.cpp

Functions.cpp

bool test(bool x) < return x; >bool test(double x) < return x >0; > double add( double x, double y) < return x + y; >double add( double a, double b, double c)

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

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

Если условие истинно, то выполняется действие 1, если же условие было ложным — выполняется действие 2. Следует заметить, что ветка else не является обязательной.

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

  • > — больше
  • >= — больше или равно
  • == — равно
  • != — не равно

double x = Convert.ToDouble(Console.ReadLine());

if(x == 12)
Console.WriteLine("Эй, гражданина. ");
>
else
Console.WriteLine("y article-render__block article-render__block_underlined-text-enabled article-render__block_bold-italic-combination-enabled article-render__block_unstyled" data-points="9"> Также возможно использование условного оператора if без указания явного условия. Кажется абракадаброй, но тем не менее, true (истина) — это любое значение отличное от нуля, false — ноль. Можем использовать это свойство. Вернемся к задаче вычисления значения некоторой функции. y(x) = 1 / x. В данном случае, если x равен нулю — мы снова получаем ошибку. В итоге имеем следующий код:

int x = Convert.ToInt32(Console.ReadLine());

if(x)
Console.WriteLine("Эй, гражданина. ");
>
else
Console.WriteLine("y article-render__block article-render__block_underlined-text-enabled article-render__block_bold-italic-combination-enabled article-render__block_unstyled" data-points="5"> Также любое логическое выражение можно инвертировать, то есть из истины сделать ложь, а из ложного значения истинное. Например !(1 > 10) — истина, а !(1

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

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

switch(i)
case :

break;
.
case :

break;
default:

break;
>

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

switch(city)
case "Москва":
Console.WriteLine("А денег хватит?");
break;
case "Санкт-Петербург":
Console.WriteLine("Точно не хватит!");
break;
case "Сан-Диего":
Console.WriteLine("Мечтать не вредно. ");
break;
default:
Console.WriteLine("А нам и дома хорошо.");
break;
>

Удобство использования switch-case очевидно при необходимости сравнения переменной с несколькими возможными значениями.

Сокращенная проверка

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

Например условие из первого примера примет вид:

Читайте также: