§3 Целый тип. Арифметические операции. Ассоциативность

Целый тип

Целочисленный тип данных является одним из фундаментальных типов и относится (наряду с вещественными) к арифметическим типам. Он представлен в виде знаковых и беззнаковых разновидностей типов.
Целые типы

Types              Size MIN                   MAX                   
short              2    -32768                32767                 
unsigned short     2    0                     65535                 
int                4    -2147483648           2147483647            
unsigned           4    0                     4294967295            
long               4    -2147483648           2147483647            
unsigned long      4    0                     4294967295            
long long          8    -9223372036854775808  9223372036854775807   
unsigned long long 8    0                     18446744073709551615  

Диапазоны значений, которые могут принимать эти типы, зависит от объема памяти отводимой для хранения значений целой переменной. Какой тип выбирать при решении задач? Как мы уже упоминали тип int используется по умолчанию. Размер типа int равен машинному слову, следовательно работа с данным типом будет осуществляться максимально эффективно. Но что если максимального значения этой переменной тоже станет недостаточным? В таком случае нужно выбирать тип “шире” или переходить к длинной арифметике с массивами (эту тему мы будем обсуждать позднее). В C++ ответственность соблюдения выхода за границы диапазона типа лежит целиком на разработчике алгоритма.

Другие способы инициализации

На прошлом уроке мы показали как можно инициализировать переменную с помощью операции =. Но это не единственная возможность. В C++11 появилась списочная инициализация с помощью фигурных скобок. Этот способ имеет преимущества, так как контролирует потерю точности при инициализации. К тому же он позволяет инициализировать переменную начальным значением (первая строка в примере ниже), а это в свою очередь может предупредить использование неинициализированного объекта.

int a {};
int b = {10};
int c(0);

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

int b = 10, c = 5;
int a = (2 * b - 4) / c - 1;

Арифметические операции. Линейная нотация математических формул

В C++ используются операции, которые реализуют все основные арифметические действия: сложение, вычитание, умножение, деление (для целых и действительных типов), а также взятие остатка отделения (только для целых типов). Эти операции являются бинарными, т. е. используют два операнда. Операнд (operand) ― это аргумент операции. В выражении один или несколько операндов связаны (по аналогии с математическим способом записи) операциями. В простейшем случае операнд может быть литералом, константой или переменной. В более общем случае — это выражение (в котором тоже свои операнды). В одно выражение могут входить операнды разных числовых типов.
9
Операции имеют приоритет, иначе говоря – старшинство. Операции *, /, % имеют более высокий (и при этом равный) приоритет, чем операции + и -. Для изменения порядка вычисления используются ().
Любое арифметическое выражение (имеется ввиду математическая формула) должно быть представлено в линейной нотации с помощью функций и операций языка C++. Приведем пример. Дана формула нахождения одного из двух корней квура:

 x_1=\frac{-b+\sqrt{b^2-4ac}}{2a}

Представить формулу в линейной нотации

x1 = (-b + sqrt(b * b - 4 * a * c)) / (2 * a) // или
x1 = (-b + sqrt(b * b - 4 * a * c)) / 2 / a

Здесь sqrt – функция библиотеки cmath (извлечение квадратного корня). Для её использования требуется подключить эту библиотеку.

Ассоциативность операций

Операции обладают как приоритетом, так и ассоциативностью. Ассоциативность определяет порядок вычислительных действий в выражении. Так операция присваивания ассоциативна справа, то есть вычисляется справа налево. Арифметические операции (имеющие равный приоритет), наоборот, обладают ассоциативностью слева! Поэтому, если мы в выражении:
(n % 10) * ((n / 10) % 10) * (n / 100)
опустим скобки, то вычисляться выражение будет (при введенном числе 236) в следующем порядке:

(n % 10) => 6
(n % 10 * (n / 10)) => 138
(n % 10 * (n / 10)) % 10 => 8
((n % 10 * (n / 10)) % 10) * n => 1888
(((n % 10 * (n / 10)) % 10) * n) / 100 => 18

То есть, при выводе даст нам неверный ответ – 18, а должно быть – 36! Ещё раз обращаем внимание на применение скобок.

Сокращенная форма присваивания

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

Полная форма    Краткая форма
a = a + b       a += b
a = a - b       a -= b
a = a * b       a *= b
a = a / b       a /= b
a = a % b       a %= b

Пробелы между операцией "=" и арифметической операцией не допускаются.

Постфиксный и префиксный инкремент и декремент

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

Формы
префиксная   постфиксная
инкремент
++a          a++
декремент
--a          a--

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

#include <iostream>
using namespace std;

int main() {
	int a = 5;
	cout << a++ << "\n"
	     << a   << "\n"
	     << ++a << endl;
	return 0;
}

На выводе:

5
6
7

Сначала выводится выражение с постфиксным инкрементом. Будет выведено первоначальное значение переменной (5), а затем происходит приращение и в следующей строке выводится уже измененное значение (5+1). В третьей строке вывода – сначала происходит приращение (6+1, префиксный инкремент), а затем вывод измененного значения (7).
Операции декремента --a и a-- эквивалентны присваиваниям a = a - 1 или a -= 1 . Аналогично операции инкремента ++a и a++ эквивалентны присваиваниям a = a + 1 или a += 1 . Однако, поскольку операции инкремента и декремента применяются довольно часто их реализация отличается от операций с присваиванием, что позволяет им работать быстрее аналогичных операций с присваиванием.
Унарные операции инкремента и декремента обладают высшим приоритетом. Они могут применяться в различных выражениях, но делать это нужно чрезвычайно осторожно, поскольку очередность выполнения операций становится не очевидной. Типичное применение этих операций – счетчики циклов.

Операция взятия остатка от деления "%"

Для целых типов предусмотрены два вида операций деления:
1. / – целочисленное деление с отбрасыванием дробной части и
2. % – получение остатка от деления
С помощью операций целочисленного деления можно решать целый класс задач в которых требуется получать разряды числа. Например. Постановка задачи:
Дано трехзначное число n. Получить сумму и произведение цифр составляющих это число.
Программа 3.2

#include <iostream>
using namespace std;

int main() {
	int n;
	cout << "n = "; cin >> n;
	cout << "Сумма цифр числа = "
		 << n % 10 + (n / 10) % 10 + n / 100
		 << "\n"
		 << "Произведение цифр = "
		 << (n % 10) * ((n / 10) % 10) * (n / 100)
		 << endl;
	return 0;
}

Результат в консоли:

n = 236
Сумма цифр числа = 11
Произведение цифр = 36

Однако, операция взятия остатка от деления, с точки зрения математики, не совсем корректна. Это иллюстрирует следующий пример:
Программа 3.3

#include <iostream>
using namespace std;

int main() {
	int a = 7, b = 2, c = -7, d = -2;
	cout << a % b << "\n"
		 << a % d << "\n"
		 << c % d << "\n"
		 << c % b << endl;
	return 0;
}

Вывод программы:

1
1
-1
-1

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

Вопросы

  1. В чем отличие префиксной формы инкремента и декремента от постфиксной?
  2. Что такое приоритет и ассоциативность?
  3. Когда появляется необходимость использовать скобки в выражениях?
  4. Когда можно применить сокращенную форму присваивания?
  5. В чем отличие явного преобразования типа от неявного?
  6. Какая форма явного преобразования типа используется в С++?
  7. Приведите примеры использования знаковых и беззнаковых типов.

Практические задания

1. Дано трехзначное число n. Получить новое значение n образованное прочтением исходного числа справа налево (то есть поменять разряды числа. Например 123 -> 321).
2. Дни недели пронумерованы следующим образом: 0 — воскресенье, 1 — понедельник, 2 — вторник, …, 6 — суббота. Дано целое число K, лежащее в диапазоне 1–365. Определить номер дня недели для K-го дня года, если известно, что в этом году 1 января было понедельником. (См. ссылку).

Ссылки

» Как узнать номер дня недели?


Print Friendly, PDF & Email

Добавить комментарий