- •Введение
- •1. Этапы и проблемы решения задач с использованием компьютера
- •Алфавит языка
- •Ключевые слова
- •Идентификаторы
- •Знаки операций
- •Константы
- •Комментарии
- •2.3. Структура и основные элементы программы
- •2.4. Трансляция программ и их выполнение
- •3. Стандартные (базовые) типы данных, операции и выражения
- •3.1. Типы данных, переменные и константы Понятие типов данных
- •Классификация простых предопределенных типов данных
- •Переменные, константы
- •3.2. Целочисленные типы данных
- •3.3. Вещественные типы данных
- •3.4. Логический тип данных
- •3.5. Символьный тип данных
- •3.6. Операции и выражения
- •Преобразования типов данных
- •Операция присваивания
- •Арифметические операции
- •Операции отношения
- •Логические операции
- •Поразрядные (битовые) операции
- •Операции составного присваивания
- •Условная операция
- •Операция sizeof
- •Приоритеты рассмотренных операций
- •3.7. Ввод и вывод простых типов данных
- •Вывод текстовых строк
- •Ввод/вывод арифметических типов данных
- •Форматирование ввода / вывода
- •4.1. Идеи структурного программирования
- •Условная инструкция (if)
- •Инструкция множественного выбора (switch)
- •Цикл с предусловием (while)
- •Цикл с постусловием (do while)
- •Итерационный цикл (for)
- •Инструкции перехода
- •5. Приемы программирования циклов
- •5.1. Рекуррентные вычисления
- •5.2. Инвариант цикла
- •6. Массивы
- •6.1. Понятие массива
- •6.2. Объявление массивов Объявление одномерных массивов
- •Объявление многомерных массивов
- •6.3. Ввод-вывод массивов
- •Вывод массивов
- •Ввод массивов
- •6.4. Текстовые строки как массивы символов
- •Определение текстовой строки
- •Ввод текстовых строк с клавиатуры
- •Обработка текстовых строк
- •Массивы текстовых строк
- •7. Разработка программ при работе с массивами
Операции отношения
Операции этой группы служат для сравнения значений. Сюда входят следующие операции:
==- равно;
!=- не равно;
> - больше;
>=- больше или равно;
<- меньше;
<= - меньше или равно.
Все эти операции бинарные. В качестве операндов могут быть использованы выражения любых арифметических типов данных. Результат этих операций всегда логического типа (bool).
Примеры:
a == b, a != b, a > 10, (a - 3) >= (b + 10).
Логические операции
Эти операции используются при построении сложных логических выражений. В эту группу входят 3 операции:
! - логическое отрицание (логическое НЕ);
&& - конъюнкция (логическое И);
|| - дизъюнкция (логическое ИЛИ).
Первая операция унарная, две остальные – бинарные. Операнды – выражения любого арифметического типа данных, значения которых интерпретируются как значения логического типа (отличное от 0 значение – true; 0 -false) . Результат этих операций - логического типа.
Правила записи и результаты выполнения логических операций приведены в следующей таблице:
a |
b |
!a |
a && b |
a || b |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
Пусть, например, имеется математическое неравенство: 0 < x < 10. На языкеC++ это неравенство следует записывать так:(0 < x) && (10 > x)или(х > 0) && (x < 10). А математическое неравенство0 > x > 10 должно выглядеть следующим образом:(0 > x) || (10 < x)или(х < 0) || (x > 10).
Особенностью выполнения операций &&и||является то, что второй операнд (в правой части операций) вычисляется не всегда. Он вычисляется только в том случае, если значения первого операнда недостаточно для получения результата операций&&или||.
Например. Если в выражении(a + 10) && (b – 1)значение первого (левого) операндаa + 10 равно0(false) (это будет при значенииa = -10), то вычисление второго (правого) операндаb – 1 не выполняется, так как и без его вычисления, значение результата операции&& уже известно – этоfalse. А в выражении(a + 10) || (b – 1)второй операнд не будет вычисляться в том случае, если первый операнд не равен0 – в этом случае результат операции|| и так уже известен – он равенtrue.
Поразрядные (битовые) операции
Побитовые операции рассматривают операнды как упорядоченные наборы битов, каждый бит может иметь одно из двух значений - 0 или 1 (наборы двоичных значений). Такие операции позволяют программисту манипулировать значениями отдельных битов. Объект, содержащий набор битов, иногда называют битовым вектором. Он позволяет компактно хранить набор флагов - переменных, принимающих значение "да" "нет".
Операции сдвига-<<и>>- бинарные операции. Операнды целого типа. Результат также целого типа. Формат записи:
< Операнд 1 > << < Операнд 2 > - сдвиг влево
< Операнд 1 > >> < Операнд 2 > - сдвиг вправо
Операции выполняют копирование битов двоичного представления первого операнда с сдвигом на количество разрядов, указанное во втором операнду, в соответствующем направлении.
Значение второго операнда должно быть больше или равно 0 и меньше количества двоичных разрядов первого операнда, иначе результат выполнения операций не гарантирован (зависит от реализации, но обычно равен 0).
Примеры:
unsigned a = 20, n = 3, r;
r = a << n;
cout << r << endl; // На экран выведено 160
r = a >> n;
cout << r << endl; // На экран выведено2
Иллюстрация:
Номер разряда: 31 30 … 8 7 6 5 4 3 2 1 0
Значение a: 0 0 … 0 0 0 0 1 0 1 0 0 = 20
Операция: a << n
Значение r: 0 0 … 0 1 0 1 0 0 0 0 0 = 160
Операция: a >> n
Значение r: 0 0 … 0 0 0 0 0 0 0 1 0 = 2
Операция сдвига влево осуществляет перемещение битов левого операнда aв сторону больших разрядов на количество разрядов, равное значению правого операндаn. Это эквивалентно умножению значенияaна 2 в степениn (20 * 8 = 160).
Операция сдвига вправо осуществляет перемещение битов левого операнда aв сторону меньших разрядов на количество разрядов, равное значению правого операндаn. Это эквивалентно делению значенияaна 2 в степениn (целочисленное деление 20 / 8 = 2).
Используя операцию сдвига влево очень просто получить любую целую степень двойки в диапазоне степеней равной количеству двоичных разрядов правого операнда без 1. Например, так:
1U << 20- равно2в степени20, то есть1048576
При сдвиге влево (в сторону старших разрядов), освобождающиеся младшие разряды замещаются 0 (нулями). При сдвиге вправо возможны две ситуации: если первый операнд беззнаковый (unsigned), то освобождающиеся старшие разряды замещаются 0; если же первый операнд знаковый, то освобождающиеся старшие разряды замещаются либо знаковым разрядом, либо 0 (нет гарантии - зависит от реализации).
Поразрядные логические операции
К этой группе операций относятся:
~- побитовое отрицание (побитовое НЕ) - унарная операция;
&- побитовая конъюнкция (побитовое И) - бинарная операция;
|- побитовая дизъюнкция (побитовое ИЛИ) - бинарная операция;
^- побитовое исключающее ИЛИ - бинарная операция.
Операндами этих операций целочисленных типов данных. Результат также целочисленный.
Операция побитовое отрицание (~) осуществляет инвертирование всех байтов двоичного представления своего операнда. Например:
int a = 14, r;
r = ~a;
cout << r << endl; // На экран выведено -15
Иллюстрация:
Номер разряда: 31 30 … 8 7 6 5 4 3 2 1 0
Значение a: 0 0 … 0 0 0 0 0 1 1 1 0 = 14
Значение r = ~a: 1 1 … 1 1 1 1 1 0 0 0 1 = -15
Остальные операции выполняют соответствующую логическую операцию над каждой парой соответствующих разрядов первого и второго операндов, интерпретируя значения двоичных разрядов как логические значения (1 - true;0 - false). Например:
int a = 14, b = 7, r;
r = a & b;
cout << r << endl; // На экран выведено 6
r = a | b;
cout << r << endl; // На экран выведено 15
r = a ^ b;
cout << r << endl; // На экран выведено 9
Иллюстрация:
Номер разряда: 31 30 … 8 7 6 5 4 3 2 1 0
Значение a: 0 0 … 0 0 0 0 0 1 1 1 0 = 14
Значение b: 0 0 … 0 0 0 0 0 0 1 1 1 = 7
Операция: a & b
Значение r: 0 0 … 0 0 0 0 0 0 1 1 0 = 6
Операция: a | b
Значение r: 0 0 … 0 0 0 0 0 1 1 1 1 = 15
Операция: a ^ b
Значение r: 0 0 … 0 0 0 0 0 1 0 0 1 = 9
Использование побитовых операций
Как уже говорилось, побитовые операции используются для обработки отдельных двоичных разрядов памяти. Для манипулирования отдельным битом необходимо научиться делать следующее:
определять значение заданного бита;
устанавливать значение заданного бита в значение 0 или 1;
инвертировать значение заданного бита.
Это можно сделать так:
unsigned a = 1234;// Целое значение, битами которого мы будем управлять
unsigned short n = 4; // Номер необходимого бита (от 0 до 31)
bool r;// Значение результата (0 или 1)
/* Узнаем, чему равен n-й бит (двоичный разряд) значенияa. Результат поместим в переменнуюr*/
r = a & (1U << n);
cout << "Разряд с номером " << n << " равен " << r << endl; // значение 1
/* Установим n-й бит (двоичный разряд) значенияa в 0. Результат поместим в переменнуюа */
a = a & (~ (1U << n));
cout << "Значение а равно " << a << endl; // значение 1218
/* Проверяем */
r = a & (1U << n);
cout << "Разряд с номером " << n << " равен " << r << endl; // значение 0
/* Возвращаем n-й бит (двоичный разряд) значенияaв1. Результат поместим в переменнуюа */
a = a | (1U << n);
cout << "Значение а равно " << a << endl; // значение 1234
/* Проверяем */
r = a & (1U << n);
cout << "Разряд с номером " << n << " равен " << r << endl; // значение 1
/* Инвертируем n-й бит (двоичный разряд) значенияa. Результат поместим в переменнуюа*/
a = a ^ (1U << n);
cout << "Значение а равно " << a << endl; // значение 1218
/* Проверяем */
r = a & (1U << n);
cout << "Разряд с номером " << n << " равен " << r << endl; // значение 0
/* Еще раз инвертируем n-й бит (двоичный разряд) значения a. Результат поместим в переменнуюа*/
a = a ^ (1U << n);
cout << "Значение а равно " << a << endl; // значение 1234
/* Проверяем */
r = a & (1U << n);
cout << "Разряд с номером " << n << " равен " << r << endl; // значение 1
Изменяя значение переменной nв диапазоне от0до31можно выполнить все эти действия над любым битом переменнойaкакое бы значение она не содержала.
Таким образом, для того, чтобы узнать, чему равен двоичный разряд с номером nв значении переменнойa, мы воспользовались выражением
a & (1U << n).
Иллюстрация вычисления этого выражения:
Номер разряда: 31 30 … 11 10 9 8 7 6 5 4 3 2 1 0
1U: 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 = 1
1U << n: 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0
Значение a: 0 0 … 0 1 0 0 1 1 0 1 0 0 1 0 = 1234
a & (1U << n): 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0 = 16
Результатом вычисления этого выражения является целое значение не равное 0. Операция присваивания этого значения логической переменнойrавтоматически преобразует целое значение16в логическое значениеtrue(т.е.1).
Если бы значение aимело бы разряд с номером4равным0, то результатом вычисления этого выражения было бы значение0. При выполнении операции присваивания это значение было бы преобразовано в логическое значениеfalse(т.е.0).
Для установки значения разряда с номером nв переменнойaв значение0используется выражение
a & (~ (1U << n)).
Иллюстрация вычисления этого выражения:
Номер разряда: 31 30 … 11 10 9 8 7 6 5 4 3 2 1 0
1U: 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 = 1
1U << n: 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0
~ (1U << n): 1 1 … 1 1 1 1 1 1 1 0 1 1 1 1
Значение a: 0 0 … 0 1 0 0 1 1 0 1 0 0 1 0 = 1234
a & (1U << n): 0 0 … 0 1 0 0 1 1 0 0 0 0 1 0 = 1218
Для установки значения разряда с номером nв переменнойaв значение1используется выражение
a | (1U << n).
Иллюстрация вычисления этого выражения:
Номер разряда: 31 30 … 11 10 9 8 7 6 5 4 3 2 1 0
1U: 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 = 1
1U << n: 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0
Значение a: 0 0 … 0 1 0 0 1 1 0 0 0 0 1 0 = 1218
a | (1U << n): 0 0 … 0 1 0 0 1 1 0 1 0 0 1 0 = 1234
Для инвертирования значения разряда с номером nв переменнойaиспользуется выражение
a ^ (1U << n).
Иллюстрация вычисления этого выражения при a = 1218:
Номер разряда: 31 30 … 11 10 9 8 7 6 5 4 3 2 1 0
1U: 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 = 1
1U << n: 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0
Значение a: 0 0 … 0 1 0 0 1 1 0 0 0 0 1 0 = 1218
a ^ (1U << n): 0 0 … 0 1 0 0 1 1 0 1 0 0 1 0 = 1234
Но, если a = 1234, то:
Номер разряда: 31 30 … 11 10 9 8 7 6 5 4 3 2 1 0
1U: 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 = 1
1U << n: 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0
Значение a: 0 0 … 0 1 0 0 1 1 0 1 0 0 1 0 = 1234
a ^ (1U << n): 0 0 … 0 1 0 0 1 1 0 0 0 0 1 0 = 1218
В C++ имеются и другие средства работы с отдельными битами, но они будут рассмотрены позже.