7.x – Резюме к главе 7 и небольшой тест

Добавлено 1 июня 2021 в 02:50

Краткое резюме

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

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

Условный оператор – это инструкция, которая указывает, следует ли выполнять некоторые связанные с ним инструкции.

Операторы if позволяют нам выполнять связанную с ним инструкцию в зависимости от того, истинно ли некоторое условие. Инструкции​ else выполняются, если связанное условие ложно. Вы можете объединить в цепочку несколько операторов if и else.

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

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

Операторы switch обеспечивают более чистый и быстрый метод выбора между несколькими совпадающими элементами. Операторы switch работают только с целочисленными типами. Метки case используются для идентификации значений для поиска совпадений с вычисляемым условием. Инструкции под меткой default выполняются, если не удается найти подходящую метку case.

Когда выполнение перетекает от инструкции под одной меткой к инструкции под последующей меткой, это называется проваливанием. Для предотвращения проваливания может использоваться инструкция break (или инструкция return). Для документирования преднамеренного проваливания может использоваться атрибут [[fallthrough]].

Операторы goto позволяют программе переходить в другое место в коде вперед или назад. Как правило, их следует избегать, поскольку они могут создавать спагетти-код, который возникает, когда программа имеет путь выполнения, напоминающий тарелку спагетти.

Циклы while позволяют программе выполнять цикл до тех пор, пока заданное условие оценивается как истинное. Условие вычисляется перед выполнением цикла.

Бесконечный цикл – это цикл, условие которого всегда истинно. Эти циклы будут повторяться бесконечно, если для их остановки не используется другая инструкция управления порядком выполнения программы.

Переменная цикла (также называемая счетчиком) – это целочисленная переменная, используемая для подсчета количества выполнений цикла. Каждое выполнение цикла называется итерацией.

Циклы do while аналогичны циклам while, но условие вычисляется после выполнения цикла, а не до.

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

Инструкции break позволяют нам выйти из switch, из циклов while, do while и for (а также из циклов for на основе диапазона, которые мы еще не рассмотрели). Инструкции continue позволяют немедленно перейти к следующей итерации цикла.

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

Аварийное завершение происходит, когда программа обнаружила непредвиденную ошибку, и ее пришлось закрыть. Для аварийного завершения может быть вызван std::abort.

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

Проверка программного обеспечения – это процесс тестирования, работает ли программное обеспечение должным образом во всех случаях. Модульный тест (юнит-тест) – это тест, предназначенный для изолированного тестирования небольшой части кода (обычно функции или вызова), чтобы гарантировать, что эта часть кода ведет себя так, как ожидалось. Фреймворки модульного тестирования могут помочь вам организовать ваши модульные тесты. Интеграционное тестирование проверяет интеграцию нескольких модулей вместе, чтобы убедиться, что они работают правильно.

Покрытие кода означает, какая часть исходного кода выполняется во время тестирования. Покрытие инструкций относится к проценту инструкций в программе, которые были проверены процедурами тестирования. Покрытие ветвей относится к проценту ветвей, которые были выполнены процедурами тестирования. Покрытие циклов (также называемое тестом 0, 1, 2) означает, что если у вас есть цикл, вы должны убедиться, что он работает правильно, когда он повторяется 0 раз, 1 раз и 2 раза.

«Счастливый путь» – это путь выполнения, который происходит, когда ошибок не обнаружено. «Печальный путь» – это тот, где возникает ошибка или состояние отказа. Неустранимая ошибка (также называемая фатальной ошибкой) – это ошибка, настолько серьезная, что программа не может продолжать работу. Программа, которая хорошо обрабатывает случаи ошибок, является надежной.

Буфер – это часть памяти, отведенная для временного хранения данных, пока они перемещаются из одного места в другое.

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

std::cerr – это выходной поток (как std::cout), предназначенный для вывода сообщений об ошибках.

Предусловие – это любое условие, которое всегда должно выполняться до выполнения некоторого сегмента кода. Инвариант – это условие, которое должно выполняться во время выполнения какого-либо компонента. Постусловие – это любое условие, которое всегда должно выполняться после выполнения некоторого кода.

Утверждение – это выражение, которое будет истинным, если в программе нет ошибки. В C++ утверждения времени выполнения обычно реализуются с помощью макроса препроцессора assert. В неотладочном коде утверждения обычно отключены. static_assert – это утверждение, которое вычисляется во время компиляции.

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

Небольшой тест

Предупреждение: с этого момента тесты становятся сложнее, но вы можете с ними справиться. Давайте пройдем эти тесты!


Вопрос 1

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

constants.h:

#ifndef CONSTANTS_H
#define CONSTANTS_H
 
namespace myConstants
{
    const double gravity { 9.8 }; // в метрах/секунда в квадрате
}
#endif

Основной файл исходного кода:

#include <iostream>
#include "constants.h"
 
double calculateHeight(double initialHeight, int seconds)
{
    double distanceFallen { myConstants::gravity * seconds * seconds / 2 };
    double heightNow { initialHeight - distanceFallen };
 
    // Проверяем, не ушли ли мы под землю
    // Если это так, устанавливаем высоту на уровне земли
    if (heightNow < 0.0)
        return 0.0;
    else
        return heightNow;
}
 
void calculateAndPrintHeight(double initialHeight, int time)
{
    std::cout << "At " << time << " seconds, the ball is at height: " << calculateHeight(initialHeight, time) << "\n";
}
 
int main()
{
    std::cout << "Enter the initial height of the tower in meters: ";
    double initialHeight;
    std::cin >> initialHeight;
	
    calculateAndPrintHeight(initialHeight, 0);
    calculateAndPrintHeight(initialHeight, 1);
    calculateAndPrintHeight(initialHeight, 2);
    calculateAndPrintHeight(initialHeight, 3);
    calculateAndPrintHeight(initialHeight, 4);
    calculateAndPrintHeight(initialHeight, 5);
	
    return 0;
}

constants.h:

#ifndef CONSTANTS_H
#define CONSTANTS_H
 
namespace myConstants
{
    const double gravity { 9.8 }; // в метрах/секунда в квадрате
}
#endif

Основной файл исходного кода:

#include <iostream>
#include "constants.h"
 
double calculateHeight(double initialHeight, int seconds)
{
    double distanceFallen { myConstants::gravity * seconds * seconds / 2 };
    double heightNow { initialHeight - distanceFallen };
 
    // Проверяем, не ушли ли мы под землю
    // Если это так, устанавливаем высоту на уровне земли
    if (heightNow < 0.0)
        return 0.0;
    else
        return heightNow;
}
 
// Возвращает true, если мяч ударился о землю, и false, если мяч всё еще падает
bool calculateAndPrintHeight(double initialHeight, int time)
{
    double currentHeight = calculateHeight(initialHeight, time);
    std::cout << "At " << time << " seconds, the ball is at height: " << currentHeight << "\n";
 
    return (currentHeight == 0.0);
}
 
int main()
{
    std::cout << "Enter the initial height of the tower in meters: ";
    double initialHeight;
    std::cin >> initialHeight;
 
    int seconds = 0;
 
    bool hitGround = false;
    do
    {
        hitGround = calculateAndPrintHeight(initialHeight, seconds++);
    }
    while (!hitGround);
 
    return 0;
}

Примечание: цикл также можно упростить как:

 while (!calculateAndPrintHeight(initialHeight, seconds++))
        ;

Поскольку calculateAndPrintHeight() и operator++ уже выполняют всю необходимую работу, нам ничего не нужно делать в теле цикла!


Вопрос 2

Простое число – это натуральное число больше 1, которое без остатка делится только на 1 и само себя. Завершите следующую программу, написав функцию isPrime() с использованием цикла for.

#include <iostream>
#include <cassert>
 
bool isPrime(int x)
{
    // напишите эту функцию с помощью цикла for
}
 
int main()
{
    assert(!isPrime(0));
    assert(!isPrime(1));
    assert(isPrime(2));
    assert(isPrime(3));
    assert(!isPrime(4));
    assert(isPrime(5));
    assert(isPrime(7));
    assert(!isPrime(9));
    assert(isPrime(11));
    assert(isPrime(13));
    assert(!isPrime(15));
    assert(!isPrime(16));
    assert(isPrime(17));
    assert(isPrime(19));
    assert(isPrime(97));
    assert(!isPrime(99));
    assert(!isPrime(99));
    assert(isPrime(13417));
 
    std::cout << "Success!";
 
    return 0;
}

#include <iostream>
#include <cassert>
 
bool isPrime(int x)
{
    int divisors{ 1 }; // Количество встреченных делителей.
                       // Начнем с 1, так как всё делится на 1.

    for (int test{ 2 }; test <= x; ++test) // проверяем каждый делитель от 2 до x
    {
        if (x % test == 0) // если x делится без остатка, увеличиваем divisors
            ++divisors;
    }
 
    return (divisors == 2); // если мы встретили только 2 делителя (1 и само число),
                            // то x должен быть простым числом
}
}
 
int main()
{
    assert(!isPrime(0));
    assert(!isPrime(1));
    assert(isPrime(2));
    assert(isPrime(3));
    assert(!isPrime(4));
    assert(isPrime(5));
    assert(isPrime(7));
    assert(!isPrime(9));
    assert(isPrime(11));
    assert(isPrime(13));
    assert(!isPrime(15));
    assert(!isPrime(16));
    assert(isPrime(17));
    assert(isPrime(19));
    assert(isPrime(97));
    assert(!isPrime(99));
    assert(!isPrime(99));
    assert(isPrime(13417));
 
    std::cout << "Success!";
 
    return 0;
}

Теги

assertC++ / CppLearnCppДля начинающихОбнаружение ошибокОбработка ошибокОбучениеПрограммированиеУсловный операторЦикл

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

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.