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

Добавлено 2 мая 2021 в 18:46

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

Наименьшая единица памяти – это двоичная цифра, также называемая битом (сокращенно от «binary digit»). Наименьший объем памяти, к которому можно получить доступ напрямую, – это байт. Современный стандарт состоит в том, что байт равен 8 битам.

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

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

void используется для обозначения отсутствия типа. В основном он используется, чтобы указать, что функция не возвращает значение.

Для разных типов данных требуется разный объем памяти, и этот объем используемой памяти может различаться в зависимости от машины. Для получения таблицы с указанием минимального размера для каждого базового типа данных смотрите раздел «4.3 – Размеры объектов и оператор sizeof».

Для получения размера типа в байтах может использоваться оператор sizeof.

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

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

Целочисленные типы фиксированной ширины созданы для определения целочисленных типов с гарантированными размерами. Используйте целочисленные типы std::int_fast#_t и std::int_least#_t, если вам нужен фиксированный размер, гарантированно равный как минимум определенному размеру. Типов std::int8_t и std::uint8_t обычно следует избегать, поскольку они имеют тенденцию вести себя как символы, а не целые числа.

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

Экспоненциальная (научная) запись – это сокращенный способ записи длинных чисел. C++ поддерживает экспоненциальную запись в сочетании с числами с плавающей запятой. Цифры в мантиссе (часть перед е) называются значащими цифрами.

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

Логический тип используется для хранения значений true (истина) или false (ложь).

Операторы if позволяют нам выполнять одну или несколько строк кода, если выполняется какое-либо условие. Несколько операторов могут быть выполнены, если они помещены внутри блока (внутри фигурных скобок). Условное выражение оператора if интерпретируется как логическое значение.

char используется для хранения значений, которые интерпретируются как символ ASCII. При использовании char будьте осторожны, чтобы не перепутать значения кода ASCII и числа. Для печати значения char как целого числа требуется использование static_cast.

Угловые скобки обычно используются в C++ для обозначения того, что требует параметризуемого типа. Это используется с static_cast, чтобы определить, в какой тип данных должен быть преобразован аргумент (например, static_cast<int>(x) преобразует x в int).

std::string предлагает простой и безопасный способ работы с текстовыми строками. Строковые литералы всегда помещаются в двойные кавычки. std::string находится в заголовке <string>.

Константа – это фиксированное значение, которое нельзя изменять. C++ поддерживает два типа констант: литеральные константы и символьные константы.

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

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

Не используйте магические числа в своем коде. Вместо этого используйте символьные константы.

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

Вопрос 1

Почему символьные константы обычно лучше, чем литеральные константы? Почему символьные константы с const/constexpr обычно лучше, чем символьные константы с #define?

Использование литеральных констант (также известных как магические числа) в программе затрудняет ее понимание и изменение. Символьные константы помогают документировать то, что на самом деле представляют числа, а изменение символьной константы при ее объявлении изменяет значение везде, где она используется. Константы с #define не отображаются в отладчике и могут создавать конфликты имен.


Вопрос 2

Выберите подходящий тип данных для переменной в каждой из следующих ситуаций. Будьте как можно более конкретными. Если ответ является целочисленным типом, выберите int, long или определенный целочисленный тип фиксированной ширины (например, std::int_fast16_t) в зависимости от диапазона значений. Если переменная должна быть константной, укажите это.

a) возраст пользователя (в годах)

int

b) нравится ли пользователю цвет, или нет

bool

c) число пи (3,14159265)

constexpr double

d) количество страниц в учебнике (предположим, что размер важен)

Поскольку книги часто могут иметь более 255 страниц, но, вероятно, никогда не могут содержать более 32 767 страниц, std::int_least16_t здесь – подходящий выбор.

e) длина дивана в метрах с точностью до 2 знаков после запятой

float

f) сколько раз вы моргнули с момента своего рождения (примечание: ответ исчисляется миллионами)

std::int_least32_t

g) пользователь выбирает пункт в меню, в котором список пунктов пронумерован буквами

char

h) год рождения (при условии, что размер важен)

std::int_least16_t. Положительные числа можно использовать для обозначения дат рождения нашей эры, а отрицательные числа – для обозначения дат рождения до нашей эры.


Вопрос 3

Примечание автора


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

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

Вот несколько советов:

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

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

Напишите следующую программу: Пользователя просят ввести 2 числа с плавающей запятой (используйте тип double). Затем пользователя просят ввести один из следующих математических символов: +, -, * или /. Программа вычисляет ответ, исходя из этих двух чисел, введенных пользователем, и печатает результаты в консоль. Если пользователь вводит неверный символ, программа не должна ничего печатать.

Пример вывода программы:

Enter a double value: 6.2
Enter a double value: 5
Enter one of the following: +, -, *, or /: *
6.2 * 5 is 31

Напишите три функции: одну для получения значения типа double, одну для получения символа арифметической операции и одну для вычисления и печати ответа.

Используйте операторы if и operator==, чтобы сравнить вводимые пользователем данные с необходимым символом арифметической операции.

#include <iostream>
 
double getDouble()
{
    std::cout << "Enter a double value: ";
    double x{};
    std::cin >> x;
    return x;
}
 
char getOperator()
{
    std::cout << "Enter one of the following: +, -, *, or / ";
    char operation{};
    std::cin >> operation;
    return operation;
}
 
void printResult(double x, char operation, double y)
{
    if (operation == '+')
        std::cout << x << " + " << y << " is " << x + y << '\n';
    else if (operation == '-')
        std::cout << x << " - " << y << " is " << x - y << '\n';
    else if (operation == '*')
        std::cout << x << " * " << y << " is " << x * y << '\n';
    else if (operation == '/')
        std::cout << x << " / " << y << " is " << x / y << '\n';
}
 
int main()
{
    double x { getDouble() };
    double y { getDouble() };
 
    char operation { getOperator() };
 
    printResult(x, operation, y);
 
    return 0;
}

Вопрос 4

Дополнительное задание: немного сложнее.

Напишите короткую программу для имитации падения мяча с башни. Для начала у пользователя следует спросить высоту башни в метрах. Предположим, что ускорение свободного падения – обычное (9,8 м/с2), и что мяч не имеет начальной скорости (мяч не движется до старта). Программа должна вывести высоту мяча над землей через 0, 1, 2, 3, 4 и 5 секунд. Мяч не должен уходить под землю (высота 0).

Ваша программа должна включать заголовочный файл с именем constants.h, который содержит символьную константу для хранения значения ускорения свободного падения (9.8).

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

\[s_{пад} = \frac{gt^2}{2}\]

где

  • sпад – расстояние падения, метры;
  • g – ускорение свободного падения, 9,8 м/с2;
  • t – время падения, секунды.

Пример вывода:

Enter the height of the tower in meters: 100
At 0 seconds, the ball is at height: 100 meters
At 1 seconds, the ball is at height: 95.1 meters
At 2 seconds, the ball is at height: 80.4 meters
At 3 seconds, the ball is at height: 55.9 meters
At 4 seconds, the ball is at height: 21.6 meters
At 5 seconds, the ball is on the ground.

Примечание: в зависимости от высоты башни мяч может не достигнуть земли за 5 секунд – это нормально. Мы улучшим эту программу, как только пройдем циклы.

Примечание. В C++ символ ^ не является символом возведения в степень. Реализуйте формулу, используя умножение вместо возведения в степень.

Примечание: не забудьте использовать литералы типа double для чисел с плавающей запятой, например. 2.0, а не 2.

constants.h:

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

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

#include "constants.h"
 
#include <iostream>
 
// получает от пользователя высоту и возвращает ее
double getTowerHeight()
{
	std::cout << "Enter the height of the tower in meters: ";
	double towerHeight{};
	std::cin >> towerHeight;
	return towerHeight;
}
 
// возвращает высоту от земли через "seconds" секунд
double calculateHeight(double towerHeight, int seconds)
{
	// По формуле: [ s = u * t + (a * t^2) / 2 ], здесь u (начальная скорость) = 0
	double distanceFallen { (gravity * (seconds * seconds)) / 2.0 };
	double currentHeight { towerHeight - distanceFallen };
 
	return currentHeight;
}
 
// печатает высоту каждую секунду, пока мяч не достигнет земли
void printHeight(double height, int seconds)
{
	if (height > 0.0)
		std::cout << "At " << seconds << " seconds, the ball is at height: " << height << " meters\n";
	else
		std::cout << "At " << seconds << " seconds, the ball is on the ground.\n";
}
 
void calculateAndPrintHeight(double towerHeight, int seconds)
{
	double height { calculateHeight(towerHeight, seconds) };
	printHeight(height, seconds);
}
 
int main()
{
	const double towerHeight { getTowerHeight() };
 
	calculateAndPrintHeight(towerHeight, 0);
	calculateAndPrintHeight(towerHeight, 1);
	calculateAndPrintHeight(towerHeight, 2);
	calculateAndPrintHeight(towerHeight, 3);
	calculateAndPrintHeight(towerHeight, 4);
	calculateAndPrintHeight(towerHeight, 5);
 
	return 0;
}

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


Вопрос 5

Найдите 3 проблемы (затрагивающие 4 строки) в следующем коде.

#include <cstdint>
#include <iostream>
 
int main()
{
  std::cout << "How old are you?\n";
 
  std::uint8_t age{};
  std::cin >> age;
 
  std::cout << "Allowed to drive a car in Texas [";
 
  if (age >= 16)
    std::cout << "x";
  else
    std::cout << " ";
 
  std::cout << "]\n";
 
  return 0;
}

Пример вывода:

How old are you?
6
Allowed to drive a car in Texas [ ]
How old are you?
19
Allowed to drive a car in Texas [x]

1) Нет причин использовать std::uint8_t (строка 8).

std::uint8_t – это необязательный тип, он существует не во всех системах. Если бы мы печатали возраст, std::cout обработал бы его как символ. Для хранения возраста следует использовать обычный int. Возраст не требует определенного целочисленного типа минимальной ширины.

2) Символы следует заключать в одинарные кавычки, а не в двойные кавычки (строки 14, 16).

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

3) Хотя значение 16 понятно из контекста, в котором оно используется, 16 можно рассматривать как магическое число. В соответствии с хорошей практикой вместо этого литерала следует определить и использовать переменную constexpr со значением 16.

Теги

C++ / CppcharconstconstexprLearnCppsize_tsizeofstd::stringvoidДля начинающихКонстантаЛитералОбучениеПрограммированиеСимвольная константаТипы данныхЦелочисленный тип данныхЧисловые типы с плавающей точкойЭкспоненциальная запись

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

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