Целочисленное деление и деление по модулю

Целочисленное деление несколько отличается от обычного. Целочисленное деление — это то же caмoe деление, которое вы изучали, когда ходили в первый класс. При делении числа 21 на число 4 (21/4) в случае целочисленного деления в ответе получается 5 и остаток 1.

Чтобы получить остаток, нужно число 21 разделить по модулю 4 (21 % 4), в результате получим остаток 1.

Операция деления по модулю иногда оказывается весьма полезной, например, если вы захотите вывести из ряда чисел каждое десятое значение. Любое число, результат деления которого по модулю 10 равен нулю, является кратным десяти, т.е. делится на 10 без остатка. Так, результат выражения 1 % 10 равен 1; 2 % 10 равен 2 и т.д.; а 10 % 10 равен 0. Результат от деления 11 % 10 снова равен 1; 12 % 10 снова равен 2; и так можно продолжать до следующего числа, кратного 10, которым окажется 20. Мы воспользуемся этим методом при рассмотрении циклов на занятии 7.

Вопросы и ответы

При делении 5 на 3 я получаю в ответе 1. В чем моя ошибка?

При делении одного целого числа на другое в качестве результата вы также получите целое число. Следовательно, 5/3 равно 1.

Для получения дробного результата нужно использовать вещественные числа. Выражение 5,0 / 3,0 даст дробный ответ: 1,66667.

Если ваш метод принимает в качестве параметров целочисленные значения, нужно привести их к типу float.

Вопросы и ответы: Выполняя операцию приведения типа переменной, вы заставляете компилятор изменить ее тип. Приэтом вы как будто говорите своемукомпилятору:"Я знаю, что делаю". Было бы неплохо, если бы это оказалось правдой, поскольку компилятор как бы отвечает вам: "Как скажете, босс: вся ответственность ложится на вас". В данном случае мы хотим сказать компилятору: "Я понимаю, что ты считаешь это значение целым, но я знаю, что делаю: это действительно вещественное значение". Для приведения типа существует два способа. Можно использовать приведение типа в старом стиле С или новый улучшенный оператор ANSIstatic_cast. Оба варианта демонстрируются в листинге 4.3.

Листинг 4.3. Приведение переменной к типу float

1: #include <iostream.h>

2:

3: void intDiv(int x, int y)

4: {

5: int z = x / y;

6: cout << "z: " << z << endl;

7: }

8:

9: void floatDiv(int x, int y)

10: {

11: float a = (float)x; // старый стиль

12: float b = static_cast<float>(y); // современный стиль

13: float c = a / b;

14:

15: cout << "c: " << c << endl;

16: }

17:

18: int main()

19: {

20: int x = 5, y = 3;

21: intDiv(x,y);

22: floatDiv(x,y);

23: return 0;

24: }

Результат:

z: 1

с: 1.66667

Анализ: В строке 20 объявляются две целочисленные переменные. В строке 21 они как параметры передаются функции intDiv, а в строке 22 — функции floatDiv. Вторая функция начинается со строки 9. В строках 11 и 12 целые значения приводятся к вещественному типу и присваиваются переменным типа float. Результат деления присваивается третьей переменной типа float в строке 13 и выводится на экран в строке 15.

Совместное использование математических операторов с операторами присваивания

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

int myAge = 5;

int temp;

temp = myAge + 2; // складываем 5 + 2 и результат помещаем в

temp myAge = temp; // значение возраста снова помещаем в myAge

Однако этот метод грешит излишествами. В языке C++ можно поместить одну и ту же переменную по обе стороны оператора присваивания, и тогда предыдущий блок сведется лишь к одному выражению:

myAge = myAge + 2;

В алгебре это выражение рассматривалось бы как бессмысленное, но в языке C++ оно читается следующим образом: добавить два к значению переменной myAge и присвоить результат переменной myAge.

Существует еще более простой вариант предыдущей записи, хотя его труднее читать:

myAge += 2;

Этот оператор присваивания с суммой (+=) добавляет r-значение к l-значению, а затем снова записывает результат в l-значение. Если бы до начала выполнения выражения переменная myAge имела значение 4, то после ее выполнения значение переменной myAge стало бы равным 6.

Помимо оператора присваивания с суммой существуют также оператор присваивания с вычитанием (-=), делением (/=), умножением (*=) и делением по модулю (%=).

Инкремент декремент

Очень часто в программах к переменным добавляется (или вычитается) единица. В языке C++ увеличение значения на 1 называется инкрементом, а уменьшение на 1 — декрементом. Для этих действий предусмотрены специальные операторы.

Оператор инкремента (++) увеличивает значение переменной на 1, а оператор декремента (--) уменьшает его на 1. Так, если у вас есть переменная С и вы хотите прирастить ее на единицу, используйте следующее выражение:

C++; // Увеличение значения С на единицу

Это же выражение можно было бы записать следующим образом:

С = С + 1;

что, в свою очередь, равносильно выражению.

С += 1;

Префикс и постфикс

Как оператор инкремента, так и оператор декремента работает в двух вариантах: префиксном и постфиксном. Префиксный вариант записывается перед именем переменной (++myAge), а постфиксный — после него (myAge++).

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

Семантика префиксного оператора следующая: инкрементируем значение, а затем считываем его. Семантика постфиксного оператора иная: считываем значение, а затем декрементируем оригинал.

На первый взгляд это может выглядеть несколько запутанным, но примеры легко проясняют механизм действия этих операторов. Если x — целочисленная переменная, значение которой равно 5, и, зная это, вы записали

int а = ++x;

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

int b = x++;

то тем самым велели компилятору присвоить переменной b текущее значение переменной x (6), а затем вернуться назад к переменной x и инкрементировать ее. В этом случае значение переменной b равно 6, но значение переменной x уже равно 7. В листинге 4.4 продемонстрировано использование обоих типов операторов инкремента и декремента.

Листинг 4.4. Примеры использования префиксных и постфиксных операторов

1: // Листинг 4.4. Демонстрирует использование

2: // префиксных и постфиксных операторов

3: // инкремента и декремента

4: #include <iostream.h>

5: int main()

6: {

7: int myAge = 39; // инициализируем две целочисленные переменные

8: int yourAge = 39;

9: cout << "I am: " << myAge << " years old.\n";

10: cout << "You are: " << yourAge << " years old\n";

11: myAge++; // постфиксный инкремент

12: ++yourAge; // префиксный инкремент

13: cout << "One year passes...\n";

14: cout << "I am: " << myAge << " years old.\n";

15: cout << "You are: " << yourAge << " years old\n";

16: cout << "Another year passes\n";

17: cout << "I am: " << myAge++ << " years old.\n";

18: cout << "You are: " << ++yourAge << " years old\n";

19: cout << "Let's print it again.\n";

20: cout << "I am: " << myAge << " years old.\n";

21: cout << "You are: " << yourAge << " years old\n";

22: return 0;

23: }

Результат:

I am 39 years old

You are 39 years old

One year passes

I am 40 years old

You are 40 years old

Another year passes

I am 40 years old

You are 41 years old

Let's print it again

I am 41 years old

You are 41 years old

Анализ: В строках 7 и 8 объявляются две целочисленные переменные и каждая из них инициализируется значением 39. Значения этих переменных выводятся в строках 9 и 10.

В строке 11 инкрементируется переменная myAge с помощью постфиксного оператора инкремента, а в строке 12 инкрементируется переменная yourAge с помощью префиксного оператора инкремента. Результаты этих операций выводятся в строках 14 и 15; как видите, они идентичны (обоим участникам нашего эксперимента по 40 лет).

В строке 17 инкрементируется переменная myAge (также с помощью постфиксного оператора инкремента), являясь при этом частью выражения вывода на экран. Поскольку здесь используется постфиксная форма оператора, то инкремент выполняется после операции вывода, поэтому снова было выведено значение 40. Затем (для сравнения с постфиксным вариантом) в строке 18 инкрементируется переменная yourAge с использованием префиксного оператора инкремента. Эта операция выполняется перед выводом на экран, поэтому отображаемое значение равно числу 41.

Наконец, в строках 20 и 21 эти же значения выводятся снова. Поскольку приращения больше не выполнялись, значение переменной myAge сейчас равно 41, как и значение переменной yourAge (все правильно: стареем мы все с одинаковой скоростью!).

Приоритеты операторов

Какое действие — сложение или умножение — выполняется первым в сложном выражении, например в таком, как это:

X = 5 + 3 * 8;

Если первым выполняется сложение, то ответ равен 8 * 8, или 64. Если же первым выполняется умножение, то ответ равен 5 + 24, или 29.

Каждый оператор имеет значение приоритета (полный список этих значений приведен в приложении А). Умножение имеет более высокий приоритет, чем сложение, поэтому значение этого "спорного" выражения равно 29.

Если два математических оператора имеют один и тот же приоритет, то они выполняются в порядке следования слева направо. Значит, в выражении

X = 5 + 3 + 8 * 9 + 6 * 4;

сначала вычисляется умножение, причем слева направо: 8*9 = 72 и 6*4 = 24. Теперь то же выражение выглядит проще:

x = 5 + 3 + 72 + 24;

Затем выполняем сложение, тоже слева направо: 5 + 3 = 8; 8 + 72 = 80; 80 + 24 = 104. Однако будьте осторожны — не все операторы придерживаются этого порядка выполнения. Например, операторы присваивания вычисляются справа налево! Но что же делать, если установленный порядок приоритетов не отвечает вашим намерениям? Рассмотрим выражение:

TotalSeconds = NumMinutesToThink + NumMinutesToType * 60

Предположим, что в этом выражении вы не хотите умножать значение переменной NumMinutesToType на число 60, а затем складывать результат со значением переменной NumMinutesToThink. Вам нужно сначала сложить значения двух переменных, чтобы получить общее число минут, а затем умножить это число на 60, получив тем самым общее количество секунд.

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

TotalSeconds = (NumMinutesToThink + NumMinutesToType) * 60

Вложение круглых скобок

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

TotalPersonSeconds = ( ( (NumMinutesToThink + NumMinutesToType) * 60) * (PeopleInTheOffice + PeopleOnVacation) )

Это сложное выражение читается изнутри. Сначала значение переменной NumMinutesToThink складывается со значением переменной NumMinutesToType, поскольку они заключены во внутренние круглые скобки. Затем полученная сумма умножается на 60. После этого значение переменной PeopleInTheOffice прибавляется к значению переменной PeopleOnVacation. Наконец, вычисленное общее количество людей умножается на общее число секунд.

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

TotalMinutes = NumMinutesToThink + NumMinutesToType; TotalSeconds = TotalMinutes * 60; TotalPeople = PeopleInTheOffice + PeopleOnVacation; TotalPersonSeconds = TotalPeople * TotalSeconds;

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

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

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

Что такое ИСТИННО

В предыдущих версиях языка C++ результаты логических выражений представлялись целочисленными значениями, но в новом стандарте ANSI введен новый тип — bool, имеющий только два возможных значения: true или false.

Любое выражение может быть рассмотрено с точки зрения его истинности или ложности. Математические выражения, возвращающие нуль, можно использовать для присвоения значения false логической переменной, а любой другой результат будет означать true.

Примечание: Многие компиляторы и раньше были сориентированы на тип bool, который внутренне представлялся с помощью типа long int и поэтому имел размер, равный четырем байтам. Ныне ANSI-совместимые компиляторы часто обеспечивают однобайтовый тип bool.

Операторы отношений

Такие операторы используются для выяснения равенства или неравенства двух значений. Выражения сравнения всегда возвращают значения true (истина) или false (ложь). Операторы отношения представлены в табл. 4.1.

Примечание: В новом стандарте ANSI предусмотрен новый тип bool, и все операторы отношений теперь возвращают значение типа bool— true и false. В предыдущих версиях языка C++ эти операторы возвращали 0 в качестве false или любое ненулевое значение (обычно 1) в качестве true.

Если одна целочисленная переменная myAge содержит значение 39, а другая целочисленная переменная yourAge — значение 40, то, используя оператор равенства (==), можно узнать, равны ли эти переменные:

myAge == yourAge; // совпадает ли значение переменной myAge со значением переменной yourAge?

Это выражение возвращает 0, или false (ложь), поскольку сравниваемые переменные не равны. Выражение

myAge > yourAge; // значение переменной myAge больше значения переменной yourAge? также возвратит 0 (или false).

Предупреждение: Многие начинающие программировать на языке C++ путают оператор присваивания (=) с оператором равенства (==). Случайное использование не того оператора может привести к такой ошибке, которую трудно обнаружить.

Всего в языке C++ используется шесть операторов отношений: равно (==), меньше (<), больше (>), меньше или равно (<=), больше или равно (>=) и не равно (!=). В табл. 4.1 не только перечислены все операторы отношений, но и приведены примеры их использования.

Рекомендуется:Помните, что операторы отношений true или false.

He рекомендуется: Не путайте оператор присваивания (=) с оператором равенства (==). Это одна из самых распространенных ошибок программирования на языке C++ — будьте начеку!

Ошибка! Недопустимый объект гиперссылки.

Оператор if

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

if(условие)

выражение;

Условие в круглых скобках может быть любым выражением, но обычно оно содержит операторы отношений. Если это выражение возвращает false, то последующий оператор пропускается. Если же оно возвращает значение true, то оператор выполняется. Рассмотрим следующий пример:

if(bigNumber > smallNumber)

bigNumber = smallNumber;

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

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

if(условие)

{

выражение 1;

выражение2;

выражениеЗ;

}

Вот простой пример применения блока выражений:

if(bigNumber > smallNumber)

{

bigNumber = smallNumber;

cout << "bigNumber: " << bigNumber << "\n";

cout << "smallNumber: " << smallNumber << "\n";

}

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

Листинг 4.5. Пример ветвления с использованием операторов отношений

1: // Листинг 4.5. Демонстрирует использование

2: // инструкции if совместно с операторами отношений

3: #include <iostream.h>

4: int main()

5: {

6: int RedSoxScore, YankeesScore;

7: cout << "Enter the score for the Red Sox: ";

8: cin >> RedSoxScore;

9:

10: cout << "\nEnter the score for the Yankees: ";

11: cin >> YankeesScore;

12:

13: cout << "\n";

14:

15: if (RedSoxScore > YankeesScore)

16: cout << "Go Sox!\n";

17:

18: if (RedSoxScore < YankeesScore)

19: {

20: cout << "Go Yankees!\n";

21: cout << "Happy days in New York!\n";

22: }

23:

24: if (RedSoxScore == YankeesScore)

25: {

26: cout << "A tie? Naah, can't be.\n";

27: cout << "Give me the real score for the Yanks: ";

28: cin >> YankeesScore;

29:

30: if (RedSoxScore > YankeesScore)

31: cout << "Knew it! Go Sox!";

32:

33: if (YankeesScore > RedSoxScore)

34: cout << "Knew it! Go Yanks!";

35:

36: if (YankeesScore == RedSoxScore)

37: cout << "Wow, it really was a tie!";

38:

39: }

40: cout << "\nThanks for telling me.\n";

41: return 0;

42: }

Результат:

Enter the score for the Red Sox: 10

Enter the score for the Yankees: 10

A tie? Naah, can't be

Give me the real score for the Yanks: 8

Knew it! Go Sox!

Thanks for telling me..

Анализ: В этой программе пользователю предлагается ввести счет очков для двух бейсбольных команд. Введенные очки сохраняются в целочисленных переменных. Значения этих переменных сравниваются оператором if в строках 15, 18 и 24.

Предупреждение: Многие начинающие программисты по невнимательности ставят точку с запятой после выражения с оператором if:

if (SomeValue < 10); SomeValue = 10;

В этом программном фрагменте было задумано сравнить значение переменной SomeValue с числом 10 и, если окажется, что оно меньше десяти, установить его равным этому числу, т.е. зафиксировать минимальное значение переменной SomeValue на уровне 10. При выполнении этого программного фрагмента обнаруживается, что переменная SomeValue (вне зависимости от ее исходного значения) всегда устанавливается равной 10. В чем же дело? А дело в том, что оператор if, вернее, связанное с ним выражение сравнения, оканчивается точкой с запятой, создавая тем самым бездействующую инструкцию. Помните, что для компилятора отступ не играет никакой роли. Приведенный выше программный фрагмент можно переписать по-другому:

if (SomeValue < 10) // проверка

; // пустое выражение, контролируемое оператором if

SomeValue = 10; // присваивание

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

Если очки одной команды больше очков другой, на экран выводится соответствующее сообщение. Если сравниваемые очки равны, программа выполняет блок выражений, который начинается в строке 25 и оканчивается в строке 38. В этом блоке снова запрашивается счет очков для команды из Нью-Йорка, после чего вновь выполняется сравнение результатов игры команд.

Обратите внимание: если начальный счет команды Yankees превышает счет команды Red Sox, то оператор if в строке 15 возвратит значение false и строка 16 не будет выполняться. Проверка же в строке 18 даст истинный результат (true) и будут выполнены выражения в строках 20 и 21. Затем с помощью оператора if в строке 24 будет проверено равенство очков; результат этого тестирования должен быть ложным (false) (иначе и быть не может, если проверка в строке 18 дала в результате значение true). В этом случае программа пропустит целый блок и перейдет сразу к выполнению строки 39.

В данном примере получение истинного результата одним оператором if не избавляет от выполнения проверок другими операторами if.

Наши рекомендации