Перечисленные типы отличаются только размерами памяти, которая требуется для хранения. Поскольку язык C++ достаточно машиннозависимый, стандарт языка гарантирует выполнение следующего условия:
1 = размер char ≤ размер short ≤ размер int ≤ размер long.
Обычно размеры типов следующие: char – 1, short – 2, int – 4, long –8, long long – 8 байт.
Со значениями целого типа можно совершать арифметические операции: +, -, *, /, %; операции сравнения: ==, !=, <=, <, >, >=; битовые операции: &, |, ^, <<, >>.
Большинство операций, таких как сложение, умножение, вычитание и операции сравнения, не вызывают проблем в понимании. Иногда после выполнения арифметических операций результат может оказаться за пределами диапазона значений, в этом случае программа выдаст ошибку.
Целочисленное деление (/) находит целую часть от деления одного целого числа на другое. Например:
–6 / 4 = 1;
–2 / 5 = 0;
–8 / 2 = 4.
Символ процента (%) обозначает операцию определения остатка от деления двух целых чисел:
–6 % 4 = 2;
–10 % 3 = 1.
Более сложные для понимания операции – битовые: & (И), | (ИЛИ), xor (исключающее ИЛИ), << (побитовый сдвиг влево), >> (побитовый сдвиг вправо).
Битовые операции И, ИЛИ и XOR к каждому биту информации применяют соответствующую логическую операцию:
–110 = 012;
–310 = 112;
–110 & 310 = 012 & 112 = 012;
–110 | 310 = 012 | 112 = 112;
–110 ^ 310 = 012 ^ 112 = 102.
Если переменная беззнакового типа, тогда результатом будет число 156, для знакового оно равно −100. Отметим, что для знаковых целых типов единица в старшем разряде битового представления – признак отрицательности числа. При этом значение, в двоичном виде состоящее из всех единиц, соот-
6
ветствует −1; если же 1 только в старшем разряде, а в остальных разрядах – нули, тогда такое число имеет минимальное для конкретного типа значения:
для char это −128.
1.2.2. Тип с плавающей точкой
Множество значений типа с плавающей точкой является подмножеством вещественных чисел, но не каждое вещественное число представимо в двоичном виде, что приводит иногда к глупым ошибкам:
float value = 0.2;
value == 0.2; // ошибка, value здесь не будет равно 0.2
Работая с переменными с плавающей точкой, программист не должен использовать операцию проверки на равенство или неравенство, вместо этого обычно используют проверку на попадание в определенный интервал:
value - 0.2 < 1e-6; // ok, подбирать интервал тоже нужно осторожно
Помимо операций сравнения тип с плавающей точкой поддерживает 4 арифметические операции, которые полностью соответствуют математическим операциям с вещественными числами.
1.2.3. Булевый тип
Данный тип состоит всего из двух значений: true (правда) и false (ложь). Для работы с переменными данного типа используют логические операции: ! (НЕ), == (равенство), != (неравенство), && (логическое И), || (логическое ИЛИ). Результат каждой операции можно найти в соответствующей таблице истинности, например:
X |
Y |
XOR |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
1.2.4. Символьный тип
Тип char – не только целый тип (обычно такой тип называют byte), но и символьный, хранящий номер символа из таблицы символов ASCII. Например, код 0x41 соответствует символу ‘A’, а 0x71 – ‘t’.
7
Иногда возникает необходимость использования символов, которые не закреплены в таблицы ASCII, и поэтому требуют для хранения более 1-го байта. Для них существует широкий символ (wchar_t).
1.2.5. Массивы
Массивы позволяют хранить последовательный набор однотипных элементов. Массив хранится в памяти непрерывным блоком, поэтому нельзя объявить массив, не указав его размер4. Чтобы объявить массив, после имени переменной пишут квадратные скобки ([]) с указанием его размера. Например:
int myArray[5]; // Массив из 5-ти элементов целого типа
Для инициализации массива значения перечисляют в фигурных скобках. Инициализировать таким образом можно только во время объявления переменной. Кстати, в этом случае необязательно указывать размер массива:
int odds[] = {1, 3, 7, 9, 11}; // Массив инициализируется 5-ю значениями
Для доступа к определенному значению в массиве (элемента массива) используют операцию доступа по индексу ([]) с указанием номера элемента (номера начинаются с 0). Например:
odds[0]; // доступ к первому элементу массива. Вернет значение 1 odds[2]; // доступ к третьему элементу. Вернет значение 7
odds[4] = 13; // пятому элементу массива присваиваем новое значение odds[5]; // ошибка доступа
1.2.6. Строки
Для записи строки программисты используют идею о том, что строка – последовательный ряд (массив) символов. Для идентификации конца строки применяют специальный символ конца строки: ‘\0’. Такие специальные символы, состоящие из обратного слэша и идентифицирующего символа, называют управляющими или escape-символами. Существуют, например, символы ‘\n’ – начало новой строки, ‘\t’ – табуляция. Для записи в строке обратного слэша применяют экранирование: перед самим знаком ставят еще один слэш: ‘\\’. Экранирование также применяют для записи кавычек.
Создадим переменную строки:
char textExample[5] = {‘T’, ‘e’, ‘s’, ‘t’, ‘\0’}; // записана строка «Test»
4 Размером массива называют количество элементов в нем.
8
Существует упрощенная запись инициализации строки:
char textExample[5] = “Test”; // Последний символ не пишется, но размер все еще 5
Не вдаваясь в подробности, приведем еще один полезный тип данных – string. Строки такого типа можно, например, складывать:
string hello = "Привет, "; string name = "Макс!";
string hello_name = hello + name; // Получится строка «Привет, Макс!»
1.2.7. Ссылка
Ссылка – объект, указывающий на какие-либо данные, но не хранящий их. Например:
int a = 2; // переменная a указывает на значение 2
int &b = a; // переменная b указывает туда же, куда и «a»
b = 4; // меняя значение b, программист меняет значение a. Теперь a = 4
int &c = 4; // ошибка, так делать нельзя, т. к. ссылке нельзя присвоить значение
Ссылка является весьма удобным средством для сокращения используемой памяти программы. Это станет очевидно, когда речь пойдет о функциях и передаче значений параметров в функции.
1.2.8. Указатель
Чтобы разобраться с этим типом данных, необходимо запомнить, что множество значений этого типа – адреса ячеек памяти, откуда начинаются данные. Также указатель поддерживает операции сложения (+), вычитания (−) и разыменовывания (*).
Адрес 0x0 означает, что указатель пуст, т. е. не указывает ни на какие данные. Этот адрес имеет свой литерал – NULL:
int *nullPtr = NULL; // пустой указатель
Сложение и вычитание адреса с целым числом или другим адресом позволяет передвигаться по памяти, доступной программе.
Операция получения данных, начинающихся по адресу, который хранится в указателе, называется разыменовывание (*). Программа считывает необходимое количество ячеек памяти и возвращает значение, хранимое в памяти.
9
int valueInMemory = 2; // задаем переменную целого типа
int *somePtr = &valueIntMemory; // копируем адрес переменной, здесь
& – возвращает адрес переменной
somePtr; // адрес ячейки памяти, например, 0x2F
*somePtr; // значение хранится в 4-х ячейках: 0x2F, 0x30, 0x31 и 0x32
Для указателей недоступна операция присваивания, которая синтаксически совпадает с операцией копирования. Другими словами, можно скопировать адрес другого указателя или адрес переменной, но определить значение адреса самому нельзя.
Указатель хранится в памяти, как и значения переменных других типов, и занимает 4 байта, поэтому можно создать указатель на указатель.
1.2.9. Перечисления
Перечисления – единственный базовый тип, задаваемый программистом. По большому счету перечисление – это упорядоченный набор именованных целочисленных констант, при этом имя перечисления будет базовым типом.
enum color {RED, BLUE, GREEN};
По умолчанию RED = 0, BLUE = 1, GREEN = 2. Поэтому значения можно сравнивать между собой, т. е. RED < BLUE < GREEN. Программист при объявлении перечисления может самостоятельно задать значения каждой из констант:
enum access {READ = 1, WRITE = 2, EXEC = 4};
Совет. Часто удобно использовать перечисления, значения которых являются степенью двойки, так как в двоичном представлении число, являющееся степенью двойки, будет состоять из одной единицы и нулей. Например:
810 = 000010002.
Результат сложения этих чисел между собой всегда однозначно указывает на то, какие числа складывались:
3710 = 001001012 = 000000012 + 000001002 + 001000002 = 110 + 410 + 3210.
1.2.10. Void
Синтаксически тип void относится к фундаментальным типам, но использовать его можно лишь как часть более сложных типов, так как объектов типа void не существует. Как правило, этот тип используется для информи-
10