русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

L- константа типа long double.


Дата добавления: 2014-04-05; просмотров: 1283; Нарушение авторских прав


Вещественные типы данных (типы данных с плавающей точкой) хранятся в памяти компьютера иначе, чем целочисленные. Внутреннее представление вещественного числа состоит из двух частей — мантиссы и порядка:

 

-1.2345e+2

| |

мантисса порядок

 

Тип float занимает 4 байта, из которых один двоичный разряд отводится под знак мантиссы, 8 разрядов под порядок и 23 под мантиссу.

Для величин типа double, занимающих 8 байт, под порядок и мантиссу отводится 11 и 52 разряда соответственно. Длина мантиссы определяет точность числа, а длина порядка — его диапазон.

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

Дать приближенную оценку точности на примерах.

Пример неточности вычислений:

 

float a = 1e30f, b;

b = a + 1e10f;

cout << b - a << endl; // На экран выведено 0

 

 

3.4. Логический тип данных

Величины логического типа могут принимать только значения true и false, являющиеся зарезервированными словами. Внутренняя форма представления значения false - О (нуль). Любое другое значение интерпретируется как true. При преобразовании к целому типу true имеет значение 1 (единица).

В памяти переменные этого типа занимают 1 байт.

Определения переменных этого типа выглядят, например, так:

 

bool b1, b2 = true, b3 (false), b4 = 1, b5 = 0;



 

Константы – литералы задаются ключевыми словами true и false.

Именованные константы этого типа особого смысла не имеют, но имеют право на существование.

3.5. Символьный тип данных

Для обозначения этого типа используется ключевое слово char.

Под величину символьного типа отводится количество байт, достаточное для размещения любого символа из набора символов для данного компьютера, что и обусловило название типа. Как правило, это 1 байт. Тип char, как и другие целые типы, может быть со знаком или без знака. В величинах со знаком можно хранить значения в диапазоне от -128 до 127. По умолчанию тип char являемся знаковым, то есть спецификатор signed использовать не обязательно. При использовании спецификатора unsigned значения могут находиться в пределах от 0 до 255. Этого достаточно для хранения любого символа из 256-символьного набора ASCII. Величины типа char могут применяться и для хранения целых чисел, не выходящих за границы указанных диапазонов.

Спецификаторы short и long к этому типу данных не применяются.

Константы - литералы символьного типа представляют собой символы, заключенные в апострофы. Например:

 

’A’ ’!’ ’#’ ’f’ ’ш’ ’я’

 

В языке C++ существует понятие управляющих или ESCAPE – последовательностей.

Управляющие последовательности начинаются символом обратной косой черты и служат:

· Для представления символов, не имеющих графического изображения. Например: ‘\n’ – перевод экранного курсора в начало следующей строки; ‘\t’ символ табуляции и т.д.

· Для представления некоторых специальных символов, а именно: знака косой черты, апострофа, знака вопроса и кавычки – ‘\\’, ‘\’’, ‘\?’, ‘\”’.

· Для представления любого из 256 символов таблицы ASCII с помощью его восьмеричного или шестнадцатеричного номера. Например: ‘\054’, ‘\x4A’.

Управляющая последовательность воспринимается компилятором как одиночный символ.

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

Если в последовательности цифр встречается недопустимая, она считается концом цифрового кода.

Допустимые варианты управляющих последовательностей приведены в следующей таблице:

 

Изображение Шестнадцатеричный код Назначение
Звуковой сигнал
\b Возврат на шаг
\f С Перевод страницы (формата)
\n A Перевод строки
D Возврат каретки
\t Горизонтальная табуляция
\v Вертикальная табуляция
\\ 5C Обратная косая черта
\’ Апостроф
\" Кавычка
\? 3F Вопросительный знак
\0oo 0oo - восьмеричный код символа
\xdd dd xdd - шестнадцатеричный код символа

3.6. Операции и выражения

Обработка данных выполняется с помощью операций.

Операция – это действие, осуществляемое над операндами. Например:

 

2 + 3

 

Здесь операция сложения (+) выполняется над двумя операндами (2 и 3).

В зависимости от количества операндов в языке C++ имеются унарные (с одним операндом), бинарные (с двумя операндами) и одна тернарная (с тремя операндами) операция.

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

Особенностью языка C++ является то, что некоторые операции в зависимости от контекста могут иметь разное назначение.

Ниже приведены операции характерные для рассмотренных выше арифметических типов данных. Другие операции будут рассмотрены по мере изучения соответствующих типов данных.

 

Унарные операции
Операция Описание
++   увеличение на 1 (инкремент)
-- уменьшение на 1 (декремент)
sizeof размер объекта или тапа данных в байтах
~ поразрядное отрицание
! логическое отрицание
- арифметическое отрицание (унарный минус)
+ унарный плюс
(тип) явное преобразование типов

 

Бинарные операции
Операция Описание
* умножение
/ деление
% остаток от деления
+ сложение
- вычитание
<< сдвиг влево
>> сдвиг вправо
< меньше
<= меньше или равно
> больше
>= больше или равно
== равно
!= не равно
& поразрядная конъюнкция (И)
^ поразрядное исключающее ИЛИ
| поразрядная дизъюнкция (ИЛИ)
&& логическое И
|| логическое ИЛИ

 

Особое значение имеет операция присвоения и ее модификации:

 

Операция Краткое описание
= присваивание
*= умножение с присваиванием
/= деление с присваиванием
%= остаток отделения с присваиванием
+= сложение с присваиванием
-= вычитание с присваиванием
<<= сдвиг влево с присваиванием
>>= сдвиг вправо с присваиванием
&= поразрядное И с присваиванием
|= поразрядное ИЛИ с присваиванием
^= поразрядное исключающее ИЛИ с присваиванием

 

Тернарная операция
Операция Описание
?: условная операция

 

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

Преобразования типов данных

Рассмотрим пример:

Определены переменные

 

int a = 5;

double b = 7.6;

 

В программе необходимо подсчитать их сумму a + b.

Внутреннее (машинное) представление типа int и типа double существенно различаются. Существенно различаются и процедуры сложения целых значений и процедуры сложения вещественных значений. Как же тогда сложить целое и вещественное? Выход – преобразовать оба значения к одному и тому же типу данных, а затем выполнить соответствующую операцию. Но если преобразовать значение переменной b к целому типу данных (отбросить дробную часть или округлить до ближайшего целого) результат будет равен либо 12, либо 13, то есть произошла потеря точности. А вот если сначала преобразовать значение a к типу double и сложить их как вещественные значения, тогда точность потеряна не будет (результат будет равен 12.6 и будет вещественного типа). На самом деле так и происходит.

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

В языке C++ различают неявное (автоматическое) и явное преобразование типов данных.

Неявное преобразование типов данных при выполнении операций, подобной рассмотренной выше (и в ряде других случаев), выполняется компилятором по определенным правилам автоматически. В чем же состоят эти правила?

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

Все данные типов char и short int преобразуются к типу int.

Если хотя бы один из операндов имеет тип double, то и другой операнд преобразуется к типу double (если он другого типа); результат вычисления имеет тип double.

Если хотя бы один из операндов имеет тип float, то и другой операнд преобразуется к типу float (если он другого типа); результат вычисления имеет тип float.

Если хотя бы один операнд имеет тип long, то и другой операнд преобразуется к типу long (если он другого типа); результат имеет тип long.

Если хотя бы один из операндов имеет тип unsigned, то и другой операнд преобразуется к типу unsigned (если его тип не unsigned); результат имеет тип unsigned.

Если ни один из случаев 1-5 не имеет места, то оба операнда должны иметь тип int; такой же тип будет и у результата.

Следует отметить, что компиляторы языка C++ достаточно свободно выполняют подобные преобразования, что может в ряде случаев привести к неожиданным результатам. Например:

 

#include <iostream>

using namespace std;

 

int main()

{

unsigned a = 5;

int b = 10;

cout << a << " - " << b << " = " << a - b << endl;

 

system("Pause");

return 0;

}

 

Результат работы программы: 5 – 10 = 4294967291

 

Таким образом, несмотря на то, что язык C++ достаточно «снисходителен» к действиям программиста, это требует от программиста еще большей дисциплины в его действиях и четких знаний нюансов языка программирования.

Для исправления ошибки в работе предыдущей программы можно, например, изменить вычисление выражения a – b следующим образом: (int) a – b или int(a) – b. В этом случае мы получим правильный результат: 5 – 10 = -5.

Здесь было использовано явное преобразование типов данных.

Явное преобразование типов данных осуществляется с помощью соответствующей операции преобразования типов данных, которая имеет один из двух следующих форматов:

 

(<тип данных>) <выражение> или <тип данных> (<выражение>)

 

Например:

 

(int) 3.14 int (3.14)

(double) a или double (a)

(long) (a + 1e5f) long (a + 1e5f)

 

Подобные преобразования имеют своим исходом три ситуации:

· преобразование без потерь;

· с потерей точности;

· с потерей данных.

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

 

short a = 100;

cout << (int) a << endl; // На экран выведено 100

cout << (char) a << endl; // Выведена буква d (ее десятичный эквивалент - 100)

cout << (float) a << endl; // На экран выведено 100

cout << (double) a << endl; // На экран выведено 100

float b = 3.14f;

cout << (double) b << endl; // На экран выведено 3.14

double d = 3.14;

cout << (float) d << endl; // На экран выведено 3.14

 

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

 

double d = 3.74;

cout << (int) d << endl; // На экран выведено 3

 

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

 

int a = -100;

cout << (unsigned) a << endl; // На экран выведено 4294967196

int a = 50000;

cout << (short) a << endl; // На экран выведено -15536

float b = 3e+9f;

cout << (int) b << endl; // На экран выведено -2147483648

double d = 3e+9;

cout << (int) d << endl; // На экран выведено -2147483648

double d = 3e+40;

cout << (float) d << endl; // На экран выведено 1.#INF - переполнение

double d = -3e+40;

cout << (float) d << endl; // На экран выведено -1.#INF - переполнение

 

Рассмотренная операция преобразования типов перешла в C++ из C. В C++ имеются свои операции преобразования типов данных. Например, рассмотренные выше преобразования в C++ можно было бы выполнить с помощью операции static_cast, имеющей следующий формат:

 

static_cast <тип данных> (выражение)

 

Например:

 

static_cast <double> (a + 2e+40f)

 

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

При явном преобразовании типов значения преобразуемых величин на самом деле не изменяются – изменяется только представление этих значений при выполнении действий над ними.

Операция присваивания

Одна из наиболее часто используемых операций. Формат операции:

 

<Переменная> = <Выражение>

| |

Операнд 1 Операнд 2

 

Например: a = b; b = 3 * a; c = 1.234;

Сначала вычисляется значение выражения с правой стороны, а затем полученное значение присваивается переменной в левой части операции (значение выражения записывается в область памяти переменной). Старое значение переменной при этом безвозвратно теряется.

При выполнении операции присваивания тип значения выражения автоматически преобразуется к типу левого операнда (к типу данных переменной в левой части). При этом возможны потери данных или точности (см. явное преобразование типов). Например:

 

#include <iostream>

using namespace std;

int main()

{

int i = 100000, k;

short j = 10000, m;

k = j; // Короткое целое преобразуется к целому без потерь

m = i; // Преобразование целого к короткому целому приводит к искажению данных

cout << k << " " << m << endl; // На экран будет выведено: 10000 -31072

k = 12500;

m = k; // Потери данных нет

cout << k << " " << m << endl; // На экран будет выведено: 12500 12500

double d = 1.8234, n1, n2;

n1 = i; // Без ошибок

n2 = j; // Без ошибок

k = d; // С потерей точности

j = d; // С потерей точности

/* На экран будет выведено: 100000 10000 1 1 */

cout << n1 << " " << n2 << " " << k << " " << j << endl;

d = 1e+100;

k = d; // С потерей данных

m = d; // С потерей данных

/* На экран будет выведено: 1е+100 -2147483648 0 */

cout << d << " " << k << " " << m << endl;

 

system("Pause");

return 0;

}

Операция присваивания в свою очередь является выражением, значением которого является значение переменной в левой части после присваивания (эту переменную часто называют L-значением (от слова left – левая сторона)). Например:

 

#include <iostream>

using namespace std;

int main()

{

int i = 7, j = 30, k;

cout << i * j << endl;

cout << (k = i * j) << endl;

cout << k << endl;

 

system("Pause");

return 0;

}

 

Результат работы программы:

 

 

Благодаря тому, что операция присваивания является выражением, в языке C++ возможно множественное присваивание:

 

<Переменная1> = <Переменная2> = … = <ПеременнаяN> = <Выражение>

 

Например:

 

#include <iostream>

using namespace std;

int main()

{

int i = 7, j = 30, k, l, m, n;

k = l = m = n = i + j;

cout << k << " " << l << " " << m << " " << n << " " << endl;

 

system("Pause");

return 0;

}

 

Результат работы программы:

 

37 37 37 37

 

Операция присваивания имеет самый низкий приоритет.

Арифметические операции

Унарный плюс (+) и унарный минус (-) Единственный операнд любого арифметического типа. Формат записи:

 

+ < Выражение >

- < Выражение >

 

Унарный плюс возвращает значение операнда без изменений.

Унарный минус (его иногда называют арифметическим отрицанием) меняет знак операнда на обратный.

 

Умножение - * - бинарная операция. Примеры записи:

 

a * b 2 * 3 a * 0.56

 

Операнды могут быть любого арифметического типа данных. Тип данных результата операции определяется правилами неявного преобразования типов.

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

 

cout << 1000000 * 1000000; // Результат: -727379968

cout << 1е20f * 1e20f; // Результат: 1.#INF – переполнение (+ бесконечность)

cout << 1е20f * -1e20f; // Результат: -1.#INF – переполнение (- бесконечность)

 

Деление - / - бинарная операция. Примеры записи:

 

a / b 2 / 3 a / 0.56

 

Если оба операнда являются целыми, то результат деления будет целым. В этом случае целый результат получается отбрасыванием дробной части от полученного реального значения (не округление). Например:

 

5 / 3 - результат равен 1.

 

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

 

5.0 / 3 или 5 / 3. или 5.0 / 3.0 или 5 / 3f или 5f / 3 или 5f / 3f или

float (5) / 3 или double (5) / 3

 

Результатом вычисления всех этих выражений будет значение 1.6666… одного из вещественных типов.

Однако, например, выражение double (5 / 3) хотя и будет вещественного типа, но его значение все равно будет равно 1, поскольку сначала будет вычислено выражение в скобках, результат которого будет целого типа и будет равен 1, а затем это значение будет приведено к вещественному типу.

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

 

cout << 1е35f / 0.0001f; // Результат: 1.#INF – переполнение (+ бесконечность)

cout << 1е20f / -0.0001f; // Результат: -1.#INF – переполнение (- бесконечность)

cout << 1е200 / 1e-200; // Результат: 1.#INF – переполнение (бесконечность)

 

Если первый операнд вещественного типа, то деление на 0 дает значение 1.#INF или -1.#INF. Если же он целого типа, возникает ошибка режима исполнения (деление на 0).

 

Остаток от деления - % - бинарная операция. Операнды только целого типа. Результат операции целого типа. Например:

 

5 % 1 - результат 0

5 % 2 - результат 1

5 % 3 - результат 2

5 % 4 - результат 1

5 % 5 - результат 0

5 % 6 - результат 5

…..

 

Если второй операнд равен 0, возникает ошибка режима исполнения (деление на 0).

 

Сложение (+) и вычитание (-) – бинарные операции. Операнды могут быть любых арифметических типов данных. Примеры записи:

 

a + b

a – b

 

Тип результата операций определяется правилами неявного преобразования типов.

При выполнении операций возможны ошибки переполнения и некорректного преобразования типов данных операндов. Например:

 

unsigned short n = 0, m;

m = n - 1;

cout << m << endl; // На экран будет выведено 65535

n = m + 1;

cout << n << endl; // На экран будет выведено 0

 

Инкремент (++) и декремент (--) – унарные операции увеличения и уменьшения операнда на 1 соответственно. Операнд может быть любого арифметического типа данных.

Операции имеют две формы – префиксную (++a, --a) и постфиксную (a++, a--).

Независимо от формы операция инкремента эквивалентна следующему оператору:

 

a = a + 1;

 

а операция декремента следующему:

 

a = a - 1;

 

Например:

 

#include <iostream>

using namespace std;

int main()

{

int i = 7, j = 10;

++ i;

j ++;

cout << i << "\t" << j << endl; // На экран выведено 8 11

-- i;

j --;

cout << i << "\t" << j << endl; // На экран выведено 7 10

 

system("Pause");

return 0;

}

 

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

Немного изменим текст программы:

 

#include <iostream>

using namespace std;

int main()

{

int i = 7, j = 10;

cout << ++ i << "\t " << j ++ << endl; // На экран выведено 8 10

cout << i << "\t " << j << endl; // На экран выведено 8 11

cout << -- i << "\t " << j -- << endl; // На экран выведено 7 11

cout << i << "\t " << j << endl; // На экран выведено 7 10

 

system("Pause");

return 0;

}

 

Разница между префиксной и постфиксной формами этих операций заключается в том, что при префиксной форме, переменная сначала меняет свое значение, а потом это измененное значение обрабатывается. В постфиксной форме значение переменной сначала обрабатывается и только потом ее значение изменяется на 1.

В первом примере операторы ++ i; j ++; просто изменяют значения переменных i и j без какой-либо другой обработки и только после окончания работы этих операторов на экран выводятся эти измененные значения. Поэтому различие между префиксной и постфиксной формами не чувствуется. То же самое происходит и при выполнении операторов -- i; j--;.

Во втором примерепрефиксные и постфиксные формыопераций инкремента и декремента участвуют в выполнении оператора вывода данных на экран. Поэтому при выполнении префиксных операций сначала происходит изменение значений переменной i, потом эти измененные значения выводятся на экран, а при выполнении постфиксных операций сначала выводятся неизмененные значения переменной j, а уже после этого производятся изменения значения этой переменной.

Еще пример:

#include <iostream>

using namespace std;

int main()

{

int i = 7, j = 10, k;

k = (++ i) + (j --); // Или k = ++ i + j --;

cout << "k = " << k << endl; // k = 18

cout << "i = " << i << endl; // i = 8

cout << "j = " << j << endl; // j = 9

 

system("Pause");

return 0;

}

 

А чему будет равно значение переменной k в этих случаях:

 

int i = 7, k;

k = (i ++) + i;

 

Здесь k = 14.

int i = 7, k;

k = (++ i) + i;

 

Здесь k = 16.

 

int i = 7, k;

k = i + (++ i);

 

Здесь k = 16.

int i = 7, k;

k = (++ i) + (++ i);

 

Здесь k = 18.

 

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

А как интерпретировать такие выражения:

 

a+++b

a---b

 

Эти выражения следует интерпретировать так:

 

(a++) + b

(a--) - b

 

Но не так:

 

a + (++b)

a - (--b)

 

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

Операции отношения

Операции этой группы служат для сравнения значений. Сюда входят следующие операции:

· == - равно;

· != - не равно;

· > - больше;

· >= - больше или равно;

· < - меньше;

· <= - меньше или равно.

 

Все эти операции бинарные. В качестве операндов могут быть использованы выражения любых арифметических типов данных. Результат этих операций всегда логического типа (bool).

Примеры:

 

a == b, a != b, a > 10, (a - 3) >= (b + 10).

Логические операции

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

· ! - логическое отрицание (логическое НЕ);

· && - конъюнкция (логическое И);

· || - дизъюнкция (логическое ИЛИ).

Первая операция унарная, две остальные – бинарные. Операнды – выражения любого арифметического типа данных, значения которых интерпретируются как значения логического типа (отличное от 0 значение – true; 0 - false) . Результат этих операций - логического типа.

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

 

a b !a a && b a || b

 

Пусть, например, имеется математическое неравенство: 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++ имеются и другие средства работы с отдельными битами, но они будут рассмотрены позже.

Операции составного присваивания

Операции этой группы перечислены в следующей таблице:

 

Операция Использование Эквивалент
*= a *= b 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 a = a << b
>>= a >>= b a = a >> b
&= a &= b a = a & b
|= a |= b a = a | b
^= a ^= b a = a ^ b

 

Общий формат записи выражений с использованием этих операций:

 

< Переменная > < Операция >= < Выражение >

 

Эквивалентом этого формата в общем случае является:

 

< Переменная > = < Переменная > < Операция > < Выражение >

 

Таким образом, выражение с использованием составного присваивания (столбец "Использование") является укороченной формой записи соответствующего эквивалентного выражения (столбец "Эквивалент").

Условная операция

Единственная в C++ тернарная операция. Формат записи:

 

< Условие > ? < Выражение 1 > : < Выражение 2 >

| | |

Операнд 1 Операнд 2 Операнд 3

 

"Условие" - любое выражение, результатом которого является число или логическое значение. Значение "Условия" трактуется как логическое значение (0 - false; любое не равное 0 значение - true). Если значение первого операнда true (не равное 0), то значение этого условного выражения будет равно значению второго операнда (Выражение 1). Если же значение первого операнда false (0), то значение этого условного выражения будет равно значению третьего операнда (Выражение 2).

Например:

 

// Переменной max присваивается наибольшее из a и b значение

int max = a > b ? a : b;

 

Условные выражения можно вкладывать друг в друга. Например:

 

// Переменной max присваивается наибольшее из a, b и c значение

int max = a > b ? (a > c ? a : c) : (b > c ? b : c);

 

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

 

cout << (a > b ? a : b) << endl; // Выводится наибольшее из a и b

Операция sizeof

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

 

sizeof < Выражение >

или

sizeof (< Тип данных >)

 

Например:

 

long a = 0;

double d;

cout << sizeof (double) << endl; // Выведено 8

cout << sizeof (5 / 3) << endl; // Выведено 4

cout << sizeof a << endl; // Выведено 4

cout << sizeof (a + 3.14f) << endl; // Выведено 4

cout << sizeof (d = a + 3.14f) << endl; // Выведено 8

Приоритеты рассмотренных операций

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

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

 

5 + 4 * 3 / 2 + 3

 

Если выполнить операции слева направо, то результат будет равен 16:

 

5 + 4 = 9

9 * 3 = 27

27 / 2 = 13

13 + 3 = 16

 

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

 

4 * 3 = 12

12 / 6 = 6

5 + 6 = 11

11 + 3 = 14

 

В арифметических и логических выражениях операции с одинаковым приоритетом выполняются слева направо. А вот выражения с операторами присваивания выполняются справа налево.

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

 

 

Операция Действие
++ Постфиксный оператор инкремента
-- Постфиксный оператор декремента
++ Префиксный оператор инкремента
-- Префиксный оператор декремента
! Логическое «НЕ»
- Унарный минус
+ Унарный плюс
тип Явное преобразование типа
sizeof Получение размерности операнда в байтах
* Умножение
/ Деление
% Остаток (деление по модулю)
+ Сложение
- Вычитание
<< Сдвиг влево
>> Сдвиг вправо
< Меньше
<= Меньше или равно
> Больше
>= Больше или равно
== Равно
!= Не равно
& Побитовое «И»
^ Побитовое исключающее «ИЛИ»
| Побитовое «ИЛИ»
&& Логическое «И»
|| Логическое «ИЛИ»
? : Условная операция
=, +=, -=, *=, /=, %=, <<=, >>=, &=, |=, ^= Простое и составные присваивания

 

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

3.7. Ввод и вывод простых типов данных

Ввод/вывод данных является неотъемлемой составляющей любой программы - без ввода исходных данных для обработки и без вывода результатов не обходится ни одна программа.

В этом разделе будут рассмотрены только некоторые аспекты организации ввода/вывода в языке C++, относящиеся к консольному вводу/выводу простых типов данных.

В языке C++ нет встроенных средств ввода/вывода — он осуществляется с помощью функций и объектов, содержащихся в стандартных библиотеках.

В C++ можно использовать два различных способа реализации ввода-вывода.

Первый способ (унаследованный от языка C) основан на использовании ряда библиотечных функций, наиболее употребимыми из которых являются функции printf и scanf. Они выполняют форматированный ввод и вывод произвольного количества величин в соответствии со строкой формата. Строка формата содержит символы, которые при выводе копируются в поток (на экран) или запрашиваются из потока (с клавиатуры) при вводе, и спецификации преобразования, начинающиеся со знака %, которые при вводе и выводе заменяются конкретными величинами. Например:

 

#include <iostream>

using namespace std;

 

int main ()

{

setlocale (0, "");

int a;

printf ("Введите целое число:\t");

scanf ("%d", &a);

printf ("Вы ввели значение:\t%d\n\n", a);

 

system ("Pause");

return 0;

}

 

Второй способ, характерный для C++, основан на использовании стандартных потоков ввода (cin) и вывода (cout). Та же самая программа в стиле C++ будет выглядеть так:

 

#include <iostream>

using namespace std;

 

int main ()

{

setlocale (0, "");

int a;

cout << "Введите целое число:\t";

cin >> a;

cout << "Вы ввели значение:\t" << a << "\n\n";

// cout << "Вы ввели значение:\t" << a << endl << endl;

system ("Pause");

return 0;

}

 

В одной и той же программе совмещать эти два способа не рекомендуется.

В дальнейшем будем использовать именно второй способ организации ввода/вывода.

При использовании потоков для вывода данных на экран используется операция <<, которая так и называется: операция вывода или операция вставки (данные "вставляются" в поток вывода).

Ввод данных с клавиатуры осуществляется с помощью операции ввода >> (операция извлечения данных из потока ввода).

Обе эти операции "знают" как осуществлять ввод и вывод стандартных простых типов данных. Более того эти операции можно "научить", как осуществлять ввод/вывод нестандартных пользовательских типов данных (перегрузка операций, которая будет рассмотрена позднее).

А сейчас перейдем к изучению приемов ввода/вывода простых стандартных типов данных.

Вывод текстовых строк

Текстовые (строковые) литералы в C++ представляются как последовательность символов, заключенная в двойные кавычки. Например:

 

"Это пример текстовой строки".

 

Вывод текстовых строк на экран осуществляется через стандартный поток вывода с помощью операции вывода <<:

 

cout << "Это пример текстовой строки";

 

Внутрь текстовых строк можно вставлять управляющие escape-последовательности. Escape-последовательности служат для управления выводом, и представляют собой специальные последовательности из двух или более символов, начинающиеся символом обратной наклонной черты - \. При этом каждая такая последовательность воспринимается компилятором как 1 символ. Примерами таких управляющих последовательностей в предыдущих программах являются \t - символ табуляции и \n - символ перевода строки (все эти последовательности приведены в разделе 3.5). С помощью Escape-последовательностей в текстовую строку можно включить любой символ с помощью его восьмеричного или шестнадцатеричного кода (в том числе и символы, которых нет на клавиатуре). Например:

 

cout << "Это символ с восьмеричным кодом 254:\t\254\n";

cout << "А это символ с шестнадцатеричным кодом xAA:\t\xAA\n";

 

На экран будут выведены две строки:

 

Это символ с восьмеричным кодом 254: ┐

А это символ с шестнадцатеричным кодом xAA: Є

 

Если на экран необходимо вывести пустую строку, достаточно вставить в поток дважды подряд управляющую последовательность \n:

 

cout << "Это первая строка\n";

cout << "\n"; // Вторая строка пустая

cout << "Это третья строка\n";

 

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

 

cout << "Это " \

"условный " \

"пример " \

"длинного " \

"текста\n";

 

или так

 

cout << "Это " "условный " "пример "

"длинного " "текста\n";

 

На экран будет выведена одна строка, после чего экранный курсор перейдет на новую строку (управляющая последовательность \n):

 

Это условный пример длинного текста

 

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

Ввод текстовых строк с клавиатуры будет рассмотрен позже.

Ввод/вывод арифметических типов данных

 

Пример простого ввода/вывода арифметических типов данных:

 

#include <iostream>

using namespace std;

 

int main ()

{

setlocale (0, "");

int i;

double d;

char c;

bool b;

 

cin >> i;

cout << i;

cin >> d;

cout << d;

cin >> c;

cout << c;

cin >> b;

cout << b;

 

system ("Pause");

return 0;

}

 

Особенности:

1. Ввод/вывод целочисленных значений осуществляется обычным образом в десятичной системе счисления.



<== предыдущая лекция | следующая лекция ==>
X12B5 -0xAF2B 0X1FF02 | Ввод вещественных типов данных можно осуществлять либо в формате с фиксированной точкой, либо в экспоненциальном формате.


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.255 сек.