Практический курс по C/C++: https://stepik.org/course/193691
Продолжаем
изучение дополнительных возможностей языка С++ и это занятие начнем с
рассмотрения некоторых дополнительных типов данных. Сразу отмечу, что в С++
присутствуют все стандартные (базовые) типы языка Си, но дополнительно введено
еще несколько:
- bool – булевый тип (1
байт), принимающий два состояния true/false;
- wchar_t – расширенный
символьный тип (2 байта в ОС Windows; 4 байта в ОС Linux);
- char8_t – символьный
тип (1 байт) для символов кодировки Unicode (UTF-8) (добавлен в
стандарте С++20);
- char16_t – символьный
тип (2 байта) для символов кодировки Unicode (UTF-16);
- char32_t – символьный
тип (4 байта) для символов кодировки Unicode (UTF-32).
Наибольший
интерес здесь представляет тип bool, которого не хватало в языке Си. Пользоваться
им очень просто. Вначале объявляется переменная этого типа, например:
а, затем, можно
изменить ее значение на true:
Переменные типа bool обычно
используют в условных выражениях. В частности, мы можем записать такую
конструкцию:
if (fl_print) {
std::cout << "Hi!" << std::endl;
}
Вообще, значение
false соответствует
целому числу 0, а true – единице. Поэтому, запись вида:
будет
эквивалентна предыдущей. Тип bool и ключевые слова
true/false введены для удобства
написания и понимания программы. Принципиально нового на уровне машинных кодов
ничего не появляется.
Следующий тип wchar_t используется
для представления символов, коды которых выходят за пределы одного байта (типа char). Как мы знаем,
это, например, могут быть символы кодировки Unicode. Если записать:
и вывести
значение кода буквы ‘Я’:
std::cout << wch << std::endl;
то увидим число 1071.
Это значение символа ‘Я’ в кодировке UTF8, т. к. текст программы сохранен
в файле в этой кодировки. Причем, смотрите, если мы попытаемся этот же символ
присвоить переменной типа char:
то компилятор
выдаст предупреждение, что код символа выходит за диапазон типа char. А вот с
латинскими символами таких проблем уже не возникает:
так как их коды
не превышают 127.
Еще одно важное
отличие языка С++ от Си состоит в том, что символьные литералы, записанные в
программе, приводятся компилятором к типу char, а не int, как это было в
языке Си. Во всем остальном, работа с отдельными символами или их
последовательностями остается прежней. Например, можно объявить строковый
литерал следующим образом:
char str[] = "Привет мир!";
и вывести это
сообщение в консоль:
std::cout << str << std::endl;
std::cout << sizeof(str) << std::endl;
Увидим строку
«Привет мир!» и число 21. Почему размер массива равен 21, а не 11 по числу
символов? Очевидно, что под русские символы здесь отводится два байта
(кодировка UTF8), поэтому и
размер становится больше.
Если мы далее
запишем:
char ch = str[0];
printf("ch = %c\n", ch);
то увидим вывод
не символа буквы ‘П’, а кракозябру. Но, если прописать:
wchar_t str[] = L"Привет мир!";
wchar_t ch = str[0];
std::cout << ch << std::endl;
то переменная ch будет содержать
корректный код первого символа строки.
Обратите
внимание, все эти изменения актуальны для кодировок, отличающихся от кодировки ASCII, где символы
латинского и русского алфавитов умещались в диапазон [0; 255], то есть, в один
байт. Если, например, используется кодовая страница Windows-1251, то
символы можно по-прежнему обрабатывать переменными типа char.
Приведение типов указателей
Следующее важное
отличие языка С++ от Си проявляется в приведении разных типов указателей друг к
другу. Пусть, например, объявлены два следующих указателя:
int* ptr_i = 0L;
char* ptr_ch = 0L;
В языке Си,
далее, мы могли бы записать команду:
и она бы
скомпилировалась с предупреждением, что типы указателей не совпадают. Язык С++
работает строже. Если типы указателей не совпадают, то компиляция останавливается
с сообщением об ошибке. Поэтому здесь нам нужно явно прописывать операцию
приведения типов:
сообщая
компилятору, что мы знаем, что делаем и адрес указателя ptr_i нужно присвоить
указателю ptr_ch.
Та же самая
ситуация возникает и с обобщенным указателем *void языка Си:
Чтобы присвоить
адрес, который хранится в ptr_void, нужно также
явно прописывать операцию приведения типов:
Напомню, что в
языке Си использование указателей *void с другими типами
даже не приводило к предупреждениям. В С++ ситуация изменилась кардинально. В
частности, из-за этого при выделении памяти с помощью функции malloc() нужно
дополнительно записывать приведение типов:
ptr_i = (int *)malloc(sizeof(int) * 5);
Далее, в С++ не
принято использовать константу NULL для указания нулевого адреса. Пишется
просто числовой литерал 0L. Начиная со стандарта С++11 был введен
специальный «нулевой» указатель с ключевым словом nullptr:
char* ptr_ch = nullptr;
printf("%p\n", ptr_ch);
В данном случае
видим все то же значение 0.
Если версия
компилятора языка С++ позволяет использовать nullptr, то
рекомендуется его применять для обозначения «нулевого» указателя.
В заключение
этого занятия отмечу еще одно популярное нововведение в языке С++ - новую форму
записи цикла for для перебора
коллекций. Давайте я покажу его на конкретном примере. Пусть у нас имеется
строка:
char msg[] = "I like C++ language";
С точки зрения
языка С++ - это коллекция (последовательность), которую можно перебирать.
Воспользуемся для этого новой формой цикла for:
for (char x : msg)
std::cout << x << " ";
После запуска
увидим:
I l
i k e C + + l a n g u a g e
То есть, массив msg был
последовательно (от начала до конца) перебран и на каждой итерации в переменную
x помещался
текущий символ. В теле цикла символы выводились через пробел. Видите, как
удобно можно проходить по коллекциям целиком, используя новую форму записи
цикла for. Причем, цикл
проходит именно по массиву, а не по строке. Например, если указать больший
размер массива:
char msg[30] = "I like C++ language";
то при выводе
дополнительно увидим значение NUL в консоли, которые присутствуют в
массиве msg. Это же
касается и всех других коллекций, например, динамических массивов (std::vector) или связных
списков (std::list) и многих
других последовательностей библиотеки STL.
На следующем
занятии мы продолжим знакомиться с дополнительными возможностями языка С++,
которые качественно отличают его от языка Си.
Практический курс по C/C++: https://stepik.org/course/193691