Символы и строки
До сих пор мы работали только с числовыми данными, как целыми, так и действительными. Но очень часто (если речь идет не о сугубо научных рассчетах) приходится оперировать с текстовой информацией. В этом листочке содержится основная информация о принципах работы с символами и текстами.
Символьный тип char
Любой текст состоит из символов. Символ — это некоторый значок, изображение. Один и тот же символ можно записать по-разному, например, два человека по-разному напишут от руки букву “A”, и даже в компьютерном представлении одна и та же буква будет выглядеть по-разному, если ее отображать разными шрифтами, при этом это будет все равно один и тот же символ. Верно и другое: разные символы могут быть записаны одинаково, например, вот две разные буквы, одна — латинского алфавита, другая — русского: “A” и “А”. Несмотря на то, что они выглядят одинаково, удобней считать их разными символами.
Итак, способ хранения текстовой информации в компьютере не связан напрямую с изображением этого текста. Вместо символов хранятся их номера — числовые коды, а вот то, как выглядит символ с данным числовым кодом на экране напрямую зависит от того, какой используется шрифт для отображения символов. При этом, разумеется, следовало бы договориться о единообразном способе кодирования символов числовыми кодами, иначе текст, записанный на одном компьютере, невозможно будет прочитать на другом компьютере.
Первоначально договорились под кодирование одного символа отвести один байт, то есть 8 бит информации. Таким образом можно было закодировать 256 различных значений, то есть в записи текста можно использовать 256 различных символов. Этого достаточно, чтобы отобразить все символы латинского алфавита, цифры, знаки препинания и некоторые другие символы. Стандарт, указывающий, какие числовые коды соответствуют каким основным символам, называется ASCII. В таблицу ASCII включены символы с кодами от 0 до 127, то есть ASCII — это семибитный код. Вот так выглядит таблица ASCII:
Char Dec Oct Hex | Char Dec Oct Hex | Char Dec Oct Hex | Char Dec Oct Hex ------------------------------------------------------------------------------------- (nul) 0 0000 0x00 | (sp) 32 0040 0x20 | @ 64 0100 0x40 | ` 96 0140 0x60 (soh) 1 0001 0x01 | ! 33 0041 0x21 | A 65 0101 0x41 | a 97 0141 0x61 (stx) 2 0002 0x02 | " 34 0042 0x22 | B 66 0102 0x42 | b 98 0142 0x62 (etx) 3 0003 0x03 | # 35 0043 0x23 | C 67 0103 0x43 | c 99 0143 0x63 (eot) 4 0004 0x04 | $ 36 0044 0x24 | D 68 0104 0x44 | d 100 0144 0x64 (enq) 5 0005 0x05 | % 37 0045 0x25 | E 69 0105 0x45 | e 101 0145 0x65 (ack) 6 0006 0x06 | & 38 0046 0x26 | F 70 0106 0x46 | f 102 0146 0x66 (bel) 7 0007 0x07 | ' 39 0047 0x27 | G 71 0107 0x47 | g 103 0147 0x67 (bs) 8 0010 0x08 | ( 40 0050 0x28 | H 72 0110 0x48 | h 104 0150 0x68 (ht) 9 0011 0x09 | ) 41 0051 0x29 | I 73 0111 0x49 | i 105 0151 0x69 (nl) 10 0012 0x0a | * 42 0052 0x2a | J 74 0112 0x4a | j 106 0152 0x6a (vt) 11 0013 0x0b | + 43 0053 0x2b | K 75 0113 0x4b | k 107 0153 0x6b (np) 12 0014 0x0c | , 44 0054 0x2c | L 76 0114 0x4c | l 108 0154 0x6c (cr) 13 0015 0x0d | - 45 0055 0x2d | M 77 0115 0x4d | m 109 0155 0x6d (so) 14 0016 0x0e | . 46 0056 0x2e | N 78 0116 0x4e | n 110 0156 0x6e (si) 15 0017 0x0f | / 47 0057 0x2f | O 79 0117 0x4f | o 111 0157 0x6f (dle) 16 0020 0x10 | 0 48 0060 0x30 | P 80 0120 0x50 | p 112 0160 0x70 (dc1) 17 0021 0x11 | 1 49 0061 0x31 | Q 81 0121 0x51 | q 113 0161 0x71 (dc2) 18 0022 0x12 | 2 50 0062 0x32 | R 82 0122 0x52 | r 114 0162 0x72 (dc3) 19 0023 0x13 | 3 51 0063 0x33 | S 83 0123 0x53 | s 115 0163 0x73 (dc4) 20 0024 0x14 | 4 52 0064 0x34 | T 84 0124 0x54 | t 116 0164 0x74 (nak) 21 0025 0x15 | 5 53 0065 0x35 | U 85 0125 0x55 | u 117 0165 0x75 (syn) 22 0026 0x16 | 6 54 0066 0x36 | V 86 0126 0x56 | v 118 0166 0x76 (etb) 23 0027 0x17 | 7 55 0067 0x37 | W 87 0127 0x57 | w 119 0167 0x77 (can) 24 0030 0x18 | 8 56 0070 0x38 | X 88 0130 0x58 | x 120 0170 0x78 (em) 25 0031 0x19 | 9 57 0071 0x39 | Y 89 0131 0x59 | y 121 0171 0x79 (sub) 26 0032 0x1a | : 58 0072 0x3a | Z 90 0132 0x5a | z 122 0172 0x7a (esc) 27 0033 0x1b | ; 59 0073 0x3b | [ 91 0133 0x5b | < 123 0173 0x7b (fs) 28 0034 0x1c | < 60 0074 0x3c | \ 92 0134 0x5c | | 124 0174 0x7c (gs) 29 0035 0x1d | = 61 0075 0x3d | ] 93 0135 0x5d | >125 0175 0x7d (rs) 30 0036 0x1e | > 62 0076 0x3e | ^ 94 0136 0x5e | ~ 126 0176 0x7e (us) 31 0037 0x1f | ? 63 0077 0x3f | _ 95 0137 0x5f | (del) 127 0177 0x7f
При этом символы с кодами, меньшими 32 — это специальные управляющие символы, которые не отображаются на экране. Например, для того, чтобы обозначить конец строки в системе Linux используется один символ с кодом 10, а в системе Windows — два подряд идущих символа с кодами 13 и 10, символы с кодами 48-57 соответствуют начертанию арабских цифр (обратите внимание, символ с кодом 0 — это вовсе не символ, отображающийся на экране, как “0”), символы с кодами 65-90 — заглавные буквы буквы латинского алфавита, а если к их кодам прибавить 32, то получатся строчные буквы латинского алфавита. В промежутках между указанными диапазонами находятся знаки препинания, математические операции и прочие символы.
Но в ASCII-таблицы нет русских букв! А также нет букв сотен других национальных алфавитов. Первоначально для отображения букв национальных алфавитов использовали вторую половину возможного значения байта, то есть символы с кодами от 128 до 255. Это приводило к множеству проблем, например, поскольку 128 значений явно недостаточно для того, чтобы отобразить символы всех национальных алфавитов (даже недостаточно для того, чтобы отобразить символы одного алфавита, например, китайской письменности. Поэтому в настоящее время для кодирования символов используется стандарт Unicode, последняя версия 5.2 которого (октябрь, 2009) включает 107361 различный символ. Естественно, для кодирования Unicode-символов недостаточно одного байта на символ, поэтому используются многобайтовые кодировки (для представления одного символа необходимо несколько байт).
Мы будем работать только с символами ASCII, поэтому для представления одного символа будет использоваться только один байт.
В языке C++ для хранения однобайтового символа используется тип данных char . Переменную типа char можно рассматривать двояко: как целое число, занимающее 1 байт и способное принимать значения от -128 до 127 (тип signed char , есть также беззнаковая модификация unsigned char , принимающая значения от 0 до 255) и как один символ текста. Само по себе определение char может оказаться как знаковым, так и беззнаковым, в зависимости от операционной системы и компилятора. Поэтому использовать тип char не рекомендуется, лучше явно указывать будет ли он знаковым ( signed ) или беззнаковым ( unsigned ).
Как и целые числа, данные типа char можно складывать, вычитать, умножать и даже делить. Но если операции умножения и деления, как правило, бессмысленны, то сложение и вычитание вполне осмысленно. Например, если к символу ‘A’ прибавить 1, то получится символ ‘B’ , а если вычесть 1, то получится символ ‘@’ . То есть в следующем фрагменте кода на экран будет выведена буква B .
char c = 'A'; c = c + 1; coutВ этом примере видно, что переменным типа char можно присваивать значения, равные ASCII кодам символов, если эти символы заключать в кавычки. То есть запись 'A' будет соответствовать символу A , или ASCII коду 65.
Также в этом примере видно, что при выводе на экран переменной типа char мы увидим изображение этого символа. Как же узнать значение ASCII-кода символа? Его не нужно узнавать, сам символ - это и есть ASCII-код. А как его вывести на экран? Очень просто - нужно преобразовать значение величины типа char к значению типа int . Например, вот так:
coutИмя типа, записанное в скобочках перед значением, это и есть оператор преобразования значения к указанному типу.
Аналогично, при считывании переменной типа char через поток cout , из потока ввода считывается один символ, переменная получает значение, равное его ASCII-коду. Например, если написать программу, содержающую строчку
char c; cin >> c;запустить ее, ввести символ A (безо всяких кавычек!), то в переменную c будет записано значение 65 - ASCII-код символа A .
Переменным типа char можно и явно присваивать числовые значения. Например, можно сделать так:
#include using namespace std; int main() < unsigned char c = 'A'; cout << c << " " << (int) c << endl; c = 126; // char можно присвоить и числовое значение cout
Эта программа выведет две строки: “ A 65 ” и “ ~ 126 ”, то есть символы с ASCII-кодами 65 (A) и 126 (~) и сами ASCII-коды.
Организовать последовательное посимвольное считывание всего входного потока можно при помощи цикла while :
#include using namespace std; int main() < char c; while (cin >> c) // Цикл пока считывание успешно < // Делаем необходимые действия, // обрабатывая символ c >return 0; >В этом примере программа будет посимвольно считывать входной поток (по умолчанию — ввод с клавиатуры), пока не встретит признак конца файла. Для того, чтобы сообщить программе о завершении входного потока при вводе с клавиатуры необходимо нажать клавиши Ctrl-d в системе Linux и Ctrl-z в системе Windows.
Эта программа при считывании данных будет игнорировать символы–разделители: пробелы, символы новой строки и табуляции. Если нужно, чтобы в переменную c считывались все символы, в том числе и разделители, то необходимо для потока ввода cin установить манипулятор noskipws при помощи инструкции:
cin >> noskipws;>S; // считать строку S с клавиатуры --> >S1>>S2>>S3; --> >S) // Цикл до тех пор, пока считывание успешно --> >. -->
Строки в языке C++
Текстовая строка - это последовательность символов. Поскольку символы в строке пронумерованы, то естественным представлением для строки был бы массив символов. Так строки и представлялись в языке C - строкой считался массив символов, а для обозначения конца строки использовался символ с ASCII-кодом 0, что позволяло хранить строки переменной длины (то есть в массиве char[n] можно было хранить строки любой длины, не превосходящей n-1 . Такой способ хранения строк порождал ряд неудобств: любая строка была ограничена по длине размером массива, а чтобы вычислить длину строки необходимо было пройти по всей строке до появления нулевого символа, то есть определение длины строки требует количество операций, пропорциональное этой длине.
В языке C++ для представления строк существует более совершенный тип данных string , в основе которого лежит такой же массив символов, завершающийся нулевым символом, но содержащий еще ряд дополнительных возможностей. Для работы со строками языка C++ необходимо в начале программы подключить описание типа string , которое находится в одноименном файле:
#include
Переменная для хранения строковых данных объявляется так:
string S;Присвоить строковой переменной некоторое константное значение можно так:
S = "Hello, world!";С записью строк в тексте программы в кавычках мы уже встречались, когда выводили текст в поток cout . Обратите внимание - константы типа char записываются в одинарных кавычках, а строки - в двойных кавычках. В частности, 'A' - это символ, а "A" - это строка, состоящая из одного символа. Поэтому переменной типа char нельзя присвоить значение "A" , поскольку они имеют несовместимые типы данных.
По сути, переменная типа string является массивом символов и с каждым символом этой строки можно работать по-отдельности, обращаясь к ним по индексу, как к элементам массива. Например:
coutДля определения длины строки есть метод size() , применяемый к строке. Он возвращает целое число - количество символов в строке. Его можно использовать так:
string S; cin >> S; coutДля начала нам понадобится две операции над строками: сложение двух строк и изменение размера строки.
Основная операция над строками - сложение: например, при сложении строк "Hello, " и "world!" получится строка "Hello, world!" . Такая операция над строками называется .
Вот пример использования конкатенации строк:
string S, S1, S2; // Объявление трех строк cout > S1; // Считали строку S1 S2 = "Hello, " // Присвоили строке значение S = S2 + S1; // Использование конкатенации coutДругая операция - изменение размера строки. Для этого существует метод resize , который применяется к строке. У метода resize есть две формы записи: с одним и с двумя параметрами. Если он вызывается с одним параметром, то этот параметр задает новую длину строки. Например, так:
string S = "abcdefg" S.resize(3); coutВторой параметр метода resize задает символ, которым будут заполнены символы в строке, если размер строки увеличивается в результате изменения размера. Например:
string S = "abc" S.resize(6, 'd'); coutПри считывании строк из входного потока считываются все символы, кроме символов–разделителей (пробелов, табуляций и новых строк), которые являются границами между строками. Например, если при выполнении следующей программы
string S1, S2, S3; // объявили 3 строки cin>>S1>>S2>>S3;ввести текст ‘ Мама мыла раму ’ (с произвольным количеством пробелов между словами), то в массив S1 будет записана строка "Мама" , в S2 — "мыла" , в S3 — "раму" .
Таким образом, организовать считывание всего файла по словам, можно следующим образом:
string s; while (cin >> s) // Цикл пока считывание успешно < // Делаем необходимые действия >Если нужно считать строку со всеми пробелами, то необходимо использовать функцию getline следующим образом:
string S; getline(cin, S);В данном случае если запустить эту программу и ввести строку "Мама мыла раму" , то именно это значение и будет присвоено строке S . Считать же весь входной поток по строкам можно при помощи следующего кода:
string s; while (getline(cin, S)) // Цикл пока считывание успешно < // Делаем необходимые действия >Ввод и вывод символьных строк в Си
Итак, строки в языке Си. Для них не предусмотрено отдельного типа данных, как это сделано во многих других языках программирования. В языке Си строка – это массив символов. Чтобы обозначить конец строки, используется символ '\0' , о котором мы говорили в прошлой части этого урока. На экране он никак не отображается, поэтому посмотреть на него не получится.
Создание и инициализация строки
Так как строка – это массив символов, то объявление и инициализация строки аналогичны подобным операциям с одномерными массивами.
Следующий код иллюстрирует различные способы инициализации строк.
char str[10]; char str1[10] = ; char str2[10] = "Hello!"; char str3[] = "Hello!";Рис.1 Объявление и инициализация строк
В первой строке мы просто объявляем массив из десяти символов. Это даже не совсем строка, т.к. в ней отсутствует нуль-символ \0 , пока это просто набор символов.
Вторая строка. Простейший способ инициализации в лоб. Объявляем каждый символ по отдельности. Тут главное не забыть добавить нуль-символ \0 .
Третья строка – аналог второй строки. Обратите внимание на картинку. Т.к. символов в строке справа меньше, чем элементов в массиве, остальные элементы заполнятся \0 .
Четвёртая строка. Как видите, тут не задан размер. Программа его вычислит автоматически и создаст массив символов нужный длины. При этом последним будет вставлен нуль-символ \0 .
Как вывести строку
Дополним код выше до полноценной программы, которая будет выводить созданные строки на экран.
#include int main(void) < char str[10]; char str1[10] = ; char str2[10] = "Hello!"; char str3[] = "Hello!"; for(int i = 0; i
Рис.2 Различные способы вывода строки на экран
Как видите, есть несколько основных способов вывести строку на экран.
- использовать функцию printf со спецификатором %s
- использовать функцию puts
- использовать функцию fputs , указав в качестве второго параметра стандартный поток для вывода stdout .
Единственный нюанс у функций puts и fputs . Обратите внимание, что функция puts переносит вывод на следующую строку, а функция fputs не переносит.
Как видите, с выводом всё достаточно просто.
Ввод строк
С вводом строк всё немного сложнее, чем с выводом. Простейшим способом будет являться следующее:
#include int main(void)
Функция gets приостанавливает работу программы, читает строку символов, введенных с клавиатуры, и помещает в символьный массив, имя которого передаётся функции в качестве параметра.
Завершением работы функции gets будет являться символ, соответствующий клавише ввод и записываемый в строку как нулевой символ.
Заметили опасность? Если нет, то о ней вас любезно предупредит компилятор. Дело в том, что функция gets завершает работу только тогда, когда пользователь нажимает клавишу ввод. Это чревато тем, что мы можем выйти за рамки массива, в нашем случае — если введено более 20 символов.
К слову, ранее ошибки переполнения буфера считались самым распространенным типом уязвимости. Они встречаются и сейчас, но использовать их для взлома программ стало гораздо сложнее.
Итак, что мы имеем. У нас есть задача: записать строку в массив ограниченного размера. То есть, мы должны как-то контролировать количество символов, вводимых пользователем. И тут нам на помощь приходит функция fgets :
#include int main(void)
Функция fgets принимает на вход три аргумента: переменную для записи строки, размер записываемой строки и имя потока, откуда взять данные для записи в строку, в данном случае — stdin . Как вы уже знаете из 3 урока, stdin – это стандартный поток ввода данных, обычно связанный с клавиатурой. Совсем необязательно данные должны поступать именно из потока stdin , в дальнейшем эту функцию мы также будем использовать для чтения данных из файлов.
Если в ходе выполнения этой программы мы введем строку длиннее, чем 10 символов, в массив все равно будут записаны только 9 символов с начала и символ переноса строки, fgets «обрежет» строку под необходимую длину.
Обратите внимание, функция fgets считывает не 10 символов, а 9 ! Как мы помним, в строках последний символ зарезервирован для нуль-символа.
Давайте это проверим. Запустим программу из последнего листинга. И введём строку 1234567890 . На экран выведется строка 123456789 .

Рис.3 Пример работы функции fgets
Возникает вопрос. А куда делся десятый символ? А я отвечу. Он никуда не делся, он остался в потоке ввода. Выполните следующую программу.
#include int main(void)
Вот результат её работы.

Рис.4 Непустой буфер stdin
Поясню произошедшее. Мы вызвали функцию fgets . Она открыла поток ввода и дождалась пока мы введём данные. Мы ввели с клавиатуры 1234567890\n ( \n я обозначаю нажатие клавиша Enter ). Это отправилось в поток ввода stdin . Функция fgets , как и полагается, взяла из потока ввода первые 9 символов 123456789 , добавила к ним нуль-символ \0 и записала это в строку str . В потоке ввода осталось ещё 0\n .
Далее мы объявляем переменную h . Выводим её значение на экран. После чего вызываем функцию scanf . Тут-то ожидается, что мы можем что-то ввести, но т.к. в потоке ввода висит 0\n , то функция scanf воспринимает это как наш ввод, и записывается 0 в переменную h . Далее мы выводим её на экран.
Это, конечно, не совсем такое поведение, которое мы ожидаем. Чтобы справиться с этой проблемой, необходимо очистить буфер ввода после того, как мы считали из него строку, введённую пользователем. Для этого используется специальная функция fflush . У неё всего один параметр – поток, который нужно очистить.
Исправим последний пример так, чтобы его работа была предсказуемой.
#include int main(void) < char str[10]; fgets(str, 10, stdin); fflush(stdin); // очищаем поток ввода puts(str); int h = 99; printf("do %d\n", h); scanf("%d",&h); printf("posle %d\n", h); return 0; >
Теперь программа будет работать так, как надо.

Рис.4 Сброс буфера stdin функцией fflush
Подводя итог, можно отметить два факта. Первый. На данный момент использование функции gets является небезопасным, поэтому рекомендуется везде использовать функцию fgets .
Второй. Не забывайте очищать буфер ввода, если используете функцию fgets .
На этом разговор о вводе строк закончен. Идём дальше.
Сохрани в закладки или поддержи проект.
Практика
Решите предложенные задачи:

Для удобства работы сразу переходите в полноэкранный режим
Исследовательские задачи для хакеров
- Проверьте как ведет себя ваш компилятор в случае переполнения буфера.
Дополнительные материалы
- пока нет
Строковые и символьные литералы (C++)
В C++ поддерживаются различные типы строк и символов, а также доступны различные способы выражения значений литералов каждого из этих типов. В исходном коде содержимое символьных и строковых литералов выражается с помощью кодировки. Универсальные имена символов и escape-символы позволяют представить любую строку, используя только основную кодировку исходного кода. Необработанные строковые литералы позволяют не использовать escape-символы и могут применяться для выражения всех типов строковых литералов. Вы также можете создавать std::string литералы, не выполняя дополнительные действия по созданию или преобразованию.
#include using namespace std::string_literals; // enables s-suffix for std::string literals int main() < // Character literals auto c0 = 'A'; // char auto c1 = u8'A'; // char auto c2 = L'A'; // wchar_t auto c3 = u'A'; // char16_t auto c4 = U'A'; // char32_t // Multicharacter literals auto m0 = 'abcd'; // int, value 0x61626364 // String literals auto s0 = "hello"; // const char* auto s1 = u8"hello"; // const char* before C++20, encoded as UTF-8, // const char8_t* in C++20 auto s2 = L"hello"; // const wchar_t* auto s3 = u"hello"; // const char16_t*, encoded as UTF-16 auto s4 = U"hello"; // const char32_t*, encoded as UTF-32 // Raw string literals containing unescaped \ and " auto R0 = R"("Hello \ world")"; // const char* auto R1 = u8R"("Hello \ world")"; // const char* before C++20, encoded as UTF-8, // const char8_t* in C++20 auto R2 = LR"("Hello \ world")"; // const wchar_t* auto R3 = uR"("Hello \ world")"; // const char16_t*, encoded as UTF-16 auto R4 = UR"("Hello \ world")"; // const char32_t*, encoded as UTF-32 // Combining string literals with standard s-suffix auto S0 = "hello"s; // std::string auto S1 = u8"hello"s; // std::string before C++20, std::u8string in C++20 auto S2 = L"hello"s; // std::wstring auto S3 = u"hello"s; // std::u16string auto S4 = U"hello"s; // std::u32string // Combining raw string literals with standard s-suffix auto S5 = R"("Hello \ world")"s; // std::string from a raw const char* auto S6 = u8R"("Hello \ world")"s; // std::string from a raw const char* before C++20, encoded as UTF-8, // std::u8string in C++20 auto S7 = LR"("Hello \ world")"s; // std::wstring from a raw const wchar_t* auto S8 = uR"("Hello \ world")"s; // std::u16string from a raw const char16_t*, encoded as UTF-16 auto S9 = UR"("Hello \ world")"s; // std::u32string from a raw const char32_t*, encoded as UTF-32 >
Строковые литералы не могут иметь префикс или u8 L , u и U префиксы для обозначения узкого символа (однобайтового или многобайтового), UTF-8, широкий символ (UCS-2 или UTF-16), кодировки UTF-16 и UTF-32 соответственно. Необработанный строковый литерал может иметь R , u8R и LR uR UR префиксы для необработанных эквивалентов этих кодировок. Чтобы создать временные или статические std::string значения, можно использовать строковые литералы или необработанные строковые литералы с суффиксом s . Дополнительные сведения см. в разделе "Строковые литералы " ниже. Дополнительные сведения о базовом наборе исходных символов, универсальных именах символов и использовании символов из расширенных кодовых страниц в исходном коде см. в разделе "Наборы символов".
Символьные литералы
Символьный литерал состоит из символьной константы. Он представлен символом, окруженным одними кавычками. Существует пять типов символьных литерала:
- Обычные символьные литералы типа char , например 'a'
- Символьные литералы UTF-8 типа char ( char8_t в C++20), например u8'a'
- Расширенные символьные литералы типа wchar_t , например L'a' .
- Символьные литералы типа char16_t UTF-16, например u'a'
- Символьные литералы типа char32_t UTF-32, например U'a'
Символ, используемый для литерала символов, может быть любым символом, за исключением зарезервированных символов обратной косой черты ( \ ), одной кавычки ( ' ) или новой строки. Зарезервированные символы можно указывать с помощью escape-последовательности. Символы можно указывать с помощью универсальных имен символов, при условии что тип является достаточно крупным для размещения символа.
Кодировка
Литералы символов кодируются по-разному на основе префикса.
- Литерал символов без префикса — обычный символьный литерал. Значение обычного символьного литерала, содержащего один символ, escape-последовательность или универсальное имя символа, которое может быть представлено в наборе символов выполнения, имеет значение, равное числовому значению его кодировки в наборе символов выполнения. Обычный литерал символов, содержащий несколько символов, escape-последовательность или универсальное имя символов, является многофакторным литералом. Многофакторный литерал или обычный символьный литерал, который не может быть представлен в наборе символов выполнения, имеет тип int , и его значение определяется реализацией. Сведения о MSVC см. в следующем разделе майкрософт.
- Символьный литерал, начинающийся с L префикса, — это символьный литерал. Значение символьного литерала, содержащего один символ, escape-последовательность или универсальное имя символов, имеет значение, равное числовому значению его кодировки в наборе символов, если литерал символов не имеет представления в наборе расширенных символов выполнения, в этом случае значение определяется реализацией. Значение расширенных символьных литералов, содержащих несколько символов, escape-последовательностей или универсальных имен символов, определяется реализацией. Сведения о MSVC см. в следующем разделе майкрософт.
- Символьный литерал, начинающийся с u8 префикса, — это символьный литерал UTF-8. Значение символьного литерала UTF-8, содержащего один символ, escape-последовательность или универсальное имя символов, имеет значение, равное значению точки кода ISO 10646, если оно может быть представлено одним блоком кода UTF-8 (соответствующим блоку элементов управления C0 и Basic Latin Юникод). Если значение не может быть представлено одним блоком кода UTF-8, программа не сформирована. Символьный литерал UTF-8, содержащий несколько символов, escape-последовательность или универсальное имя символов, является неправильно сформированным.
- Символьный литерал, начинающийся с u префикса, является литералом символов UTF-16. Значение символьного литерала UTF-16, содержащего один символ, escape-последовательность или универсальное имя символов, имеет значение, равное значению точки кода ISO 10646, если оно может быть представлено одной единицей кода UTF-16 (соответствующей базовой многоязычной плоскости). Если значение не может быть представлено одним блоком кода UTF-16, программа не сформирована. Литерал символа UTF-16, содержащий несколько символов, escape-последовательность или универсальное имя символов, является неправильно сформированным.
- Символьный литерал, начинающийся с U префикса, — это символьный литерал UTF-32. Значение символьного литерала UTF-32, содержащего один символ, escape-последовательность или универсальное имя символов, имеет значение, равное значению точки кода ISO 10646. Символьный литерал UTF-32, содержащий несколько символов, escape-последовательность или универсальное имя символов, является неправильно сформированным.
Escape-последовательности
Существует три вида escape-последовательностей: простая, восьмеричная и шестнадцатеричная. Escape-последовательности могут быть любым из следующих значений:
| Значение | escape-последовательность |
|---|---|
| новая строка | \n |
| обратная косая черта | \\ |
| горизонтальная табуляция | \t |
| вопросительный знак | ? Или\? |
| вертикальная табуляция | \v |
| одинарная кавычка | \' |
| BACKSPACE | \b |
| двойная кавычка | \" |
| Возврат каретки | \r |
| нуль-символ | \0 |
| Смена страницы | \f |
| восьмеричный | \ooo |
| оповещение (колокольчик) | \a |
| шестнадцатеричный | \xhhh |
Восьмеричная escape-последовательность — это обратная косая черта, за которой следует последовательность из одной до трех восьмеричных цифр. Восьмеричная escape-последовательность завершается на первом символе, который не является восьмеричной цифрой, если обнаружена раньше, чем третья цифра. Максимально возможное восьмеричное значение . \377
Шестнадцатеричная escape-последовательность — это обратная косая черта, за которой следует символ x , за которой следует последовательность одной или нескольких шестнадцатеричных цифр. Начальные нули пропускаются. В обычном или префиксном литерале символов u8 самое высокое шестнадцатеричное значение 0xFF. В расширенном символьном литерале с префиксом L или u максимальное шестнадцатеричное значение — 0xFFFF. В расширенном символьном литерале с префиксом U максимальное шестнадцатеричное значение — 0xFFFFFFFF.
В этом примере кода показаны некоторые примеры экранированных символов с помощью обычных литералов символов. Тот же синтаксис escape-последовательности действителен для других типов литеральных символов.
#include using namespace std; int main() < char newline = '\n'; char tab = '\t'; char backspace = '\b'; char backslash = '\\'; char nullChar = '\0'; cout /* Output: Newline character: ending Tab character: ending Backspace character:ending Backslash character: \ending Null character: ending */
Символ обратной косой черты ( \ ) — это символ продолжения строки, когда он помещается в конце строки. Если символ обратной косой черты требуется использовать как символьный литерал, необходимо ввести две косые черты подряд ( \\ ). Дополнительные сведения о символе продолжения строки см. в разделе Phases of Translation.
Только для систем Майкрософт
Чтобы создать значение из узкого многофакторного литерала, компилятор преобразует последовательность символов или символов между одними кавычками в 8-разрядные значения в 32-разрядное целое число. Несколько символов в литерале заполняют соответствующие байты по мере необходимости от высокого до низкого порядка. Затем компилятор преобразует целое число в тип назначения, следуя обычным правилам. Например, чтобы создать char значение, компилятор принимает байт низкого порядка. Для создания значения wchar_t или char16_t компилятор принимает младшее слово. Компилятор выдает предупреждение о том, что результат усекается, если какие-либо биты заданы выше назначенного байта или слова.
char c0 = 'abcd'; // C4305, C4309, truncates to 'd' wchar_t w0 = 'abcd'; // C4305, C4309, truncates to '\x6364' int i0 = 'abcd'; // 0x61626364
Восьмеричная escape-последовательность, которая, как представляется, содержит более трех цифр, рассматривается как 3-разрядная октальная последовательность, за которой следует последующие цифры в виде символов в многофакторном литерале, что может дать удивительные результаты. Например:
char c1 = '\100'; // '@' char c2 = '\1000'; // C4305, C4309, truncates to '0'
Escape-последовательности, которые, как представляется, содержат неосьмеривые символы, оцениваются как восьмеричное последовательность до последнего октационного символа, а затем остальные символы в виде последующих символов в многохарактерном литерале. Предупреждение C4125 создается, если первый не восьмеричный символ является десятичной цифрой. Например:
char c3 = '\009'; // '9' char c4 = '\089'; // C4305, C4309, truncates to '9' char c5 = '\qrs'; // C4129, C4305, C4309, truncates to 's'
Восьмеричная escape-последовательность, которая имеет более высокое значение, чем \377 вызывает ошибку C2022: value-in-decimal : слишком большой для символа.
Последовательность escape,которая, как представляется, имеет шестнадцатеричные и не шестнадцатеричные символы, оценивается как многофакторный литерал, содержащий шестнадцатеричную последовательность escape-последовательности до последнего шестнадцатеричного символа, за которым следует не шестнадцатеричные символы. Шестнадцатеричная escape-последовательность, содержащая шестнадцатеричные цифры, приводит к ошибке компилятора C2153: "шестнадцатеричные литералы должны иметь по крайней мере одну шестнадцатеричную цифру".
char c6 = '\x0050'; // 'P' char c7 = '\x0pqr'; // C4305, C4309, truncates to 'r'
Если расширенный символьный литерал, префиксированный с L несколькими диаграммами, то значение берется из первого символа, а компилятор вызывает предупреждение C4066. Последующие символы игнорируются, в отличие от поведения эквивалентного обычного многофакторного литерала.
wchar_t w1 = L'\100'; // L'@' wchar_t w2 = L'\1000'; // C4066 L'@', 0 ignored wchar_t w3 = L'\009'; // C4066 L'\0', 9 ignored wchar_t w4 = L'\089'; // C4066 L'\0', 89 ignored wchar_t w5 = L'\qrs'; // C4129, C4066 L'q' escape, rs ignored wchar_t w6 = L'\x0050'; // L'P' wchar_t w7 = L'\x0pqr'; // C4066 L'\0', pqr ignored
Раздел , посвященный майкрософт , заканчивается здесь.
универсальные имена символов
В символьных литералах и машинных (не являющихся необработанными) строковых литералах любой символ может быть представлен универсальным именем символа. Универсальные имена символов формируются префиксом \U , за которым следует восьмизначная кодовая точка Юникода или префикс \u , за которым следует четырехзначная точка кода Юникода. Все восемь или четыре знака, соответственно, должны присутствовать для создания корректного универсального имени символа.
char u1 = 'A'; // 'A' char u2 = '\101'; // octal, 'A' char u3 = '\x41'; // hexadecimal, 'A' char u4 = '\u0041'; // \u UCN 'A' char u5 = '\U00000041'; // \U UCN 'A'
Суррогатные пары
Универсальные имена символов не могут кодировать значения в диапазоне суррогатных точек кода D800-DFFF. Для суррогатных пар Юникода укажите универсальное имя символа, используя \UNNNNNNNN , где NNNNNNNN — восьмизначная кодовая точка для символа. При необходимости компилятор создает суррогатную пару.
В C++03 язык допускает только подмножество символов, представленных их универсальными именами символов, и допускает некоторые универсальные имена символов, которые фактически не представляют допустимые символы Юникода. Эта ошибка была исправлена в стандарте C++11. В C++11 в символьных и строковых литералах и идентификаторах можно использовать универсальные имена символов. Дополнительные сведения об универсальных именах символов см. в разделе Character Sets. Дополнительные сведения о Юникоде см. в статье Unicode. Дополнительные сведения о суррогатных парах см. в статье Surrogate Pairs and Supplementary Characters(Суррогатные пары и дополнительные символы).
Строковые литералы
Строковый литерал представляет последовательность символов, которые вместе образуют строку с завершающим нулем. Символы должны быть заключены в двойные кавычки. Существуют следующие типы строковых литералов.
Узкие строковые литералы
Узкий строковый литерал — это не префиксированный, разделенный двойными кавычками массив const char[n] типа, в котором n — длина массива в байтах. Обычный строковый литерал может содержать любые графические символы, за исключением двойных кавычек ( " ), обратной косой черты ( \ ) или символа новой строки. Обычный строковый литерал также может содержать перечисленные выше escape-последовательности и универсальные имена символов, которые помещаются в байте.
const char *narrow = "abcd"; // represents the string: yes\no const char *escaped = "yes\\no";
Строки в кодировке UTF-8
Строка в кодировке UTF-8 — это префиксированный, разделенный двойным кавычками массив типа, завершаемый значением NULL, где n — длина закодированного массива const char[n] в байтах. Строковый литерал с префиксом u8 может содержать любые графические символы, за исключением двойных кавычек ( " ), обратной косой черты ( \ ) или символа новой строки. Строковый литерал с префиксом u8 может также содержать перечисленные выше escape-последовательности и любые универсальные имена символов.
C++20 представляет переносимый char8_t (UTF-8 закодированный 8-разрядный тип символа Юникода). В C++20 u8 префиксы литералов указывают символы или строки char8_t вместо char .
// Before C++20 const char* str1 = u8"Hello World"; const char* str2 = u8"\U0001F607 is O:-)"; // C++20 and later const char8_t* u8str1 = u8"Hello World"; const char8_t* u8str2 = u8"\U0001F607 is O:-)";
Расширенные строковые литералы
Широкий строковый литерал — это массив констант wchar_t , префиксируемый символом null, который префиксируется " L " и содержит любой графический символ, кроме двойной кавычки ( " ), обратной косой черты ( \ ) или символа новой строки. Расширенный строковый литерал может содержать перечисленные выше escape-последовательности и любые универсальные имена символов.
const wchar_t* wide = L"zyxw"; const wchar_t* newline = L"hello\ngoodbye";
char16_t и char32_t (C++11)
В C++11 доступны символьные типы char16_t (портативный, 16-разрядный Юникод) и char32_t (32-разрядный Юникод):
auto s3 = u"hello"; // const char16_t* auto s4 = U"hello"; // const char32_t*
Необработанные строковые литералы (C++11)
Необработанный строковый литерал — это массив, завершающий значение NULL, любой тип символа, который содержит любой графический символ, включая двойную кавычку (), обратную косую черту ( " \ ) или новый символ. Необработанные строковые литералы часто применяются в регулярных выражениях, которые используют классы символов, а также в строках HTML и XML. Примеры см. в следующей статье: Bjarne Stroustrup's FAQ on C++11(Вопросы и ответы о C++11 от Бьерна Страуструпа).
// represents the string: An unescaped \ character const char* raw_narrow = R"(An unescaped \ character)"; const wchar_t* raw_wide = LR"(An unescaped \ character)"; const char* raw_utf8a = u8R"(An unescaped \ character)"; // Before C++20 const char8_t* raw_utf8b = u8R"(An unescaped \ character)"; // C++20 const char16_t* raw_utf16 = uR"(An unescaped \ character)"; const char32_t* raw_utf32 = UR"(An unescaped \ character)";
Разделитель — это определяемая пользователем последовательность до 16 символов, которая непосредственно предшествует открытию круглых скобок необработанного строкового литерала и сразу же следует за закрывающей скобкой. Например, в R"abc(Hello"\()abc" последовательность разделителей — abc , а содержимое строки — Hello"\( . Разделители можно использовать для различения необработанных строк, содержащих двойные кавычки и круглые скобки. Этот строковый литерал вызывает ошибку компилятора:
// meant to represent the string: )" const char* bad_parens = R"()")"; // error C2059
Однако ошибку можно устранить с помощью разделителя:
const char* good_parens = R"xyz()")xyz";
Вы можете создать необработанный строковый литерал, содержащий новую строку (а не экранированный символ) в источнике:
// represents the string: hello //goodbye const wchar_t* newline = LR"(hello goodbye)";
std::string литералы (C++14)
std::string литералы — это стандартные реализации определяемых пользователем литералы (см. ниже), представленные как "xyz"s (суффиксом s ). Такой строковый литерал создает временный объект типа std::string , std::wstring std::u32string или std::u16string в зависимости от указанного префикса. Если префикс не используется, как описано выше, создается. std::string L"xyz"s std::wstring создает объект . u"xyz"s создает std::u16string и U"xyz"s создает std::u32string.
//#include //using namespace std::string_literals; string str< "hello"s >; string str2< u8"Hello World" >; // Before C++20 u8string u8str2< u8"Hello World" >; // C++20 wstring str3< L"hello"s >; u16string str4< u"hello"s >; u32string str5< U"hello"s >;
Суффикс s можно также использовать в необработанных строковых литералах:
u32string str6< UR"(She said "hello.")"s >;
std::string литералы определяются в пространстве std::literals::string_literals имен в файле заголовка . Поскольку std::literals::string_literals и std::literals объявляются как встроенные пространства имен, std::literals::string_literals автоматически считается напрямую принадлежащим пространству имен std .
Размер строковых литерала
Для строк ANSI char* и других однобайтовых кодировок (но не UTF-8), размер (в байтах) строкового литерала — это число символов плюс 1 для завершающего символа NULL. Для всех других типов строк размер не связан строго с количеством символов. UTF-8 использует до четырех char элементов для кодирования некоторых единиц кода и char16_t wchar_t в кодировке UTF-16 может использовать два элемента (в общей сложности четыре байта) для кодирования одной единицы кода. В примере ниже показан размер расширенного строкового литерала в байтах.
const wchar_t* str = L"Hello!"; const size_t byteSize = (wcslen(str) + 1) * sizeof(wchar_t);
Обратите внимание, что strlen() и wcslen() не включайте размер завершающего символа NULL, размер которого равен размеру элемента типа строки: один байт в char* строке или char8_t* строке, два байта wchar_t* или char16_t* строки и четыре байта в char32_t* строках.
В версиях Visual Studio до Visual Studio 2022 версии 17.0 максимальная длина строкового литерала составляет 65 535 байт. Это ограничение применимо как к узким, так и к расширенным строковым литералам. В Visual Studio 2022 версии 17.0 и более поздних версиях это ограничение будет отменено, а длина строки ограничена доступными ресурсами.
Изменение строковых литерала
Так как строковые литералы (не включая std::string литералы) являются константами, пытаясь изменить их( например, str[2] = 'A' вызывает ошибку компилятора.
Только для систем Майкрософт
В Microsoft C++можно использовать строковый литерал для инициализации указателя на неконст char или wchar_t . Эта неконстантная инициализация разрешена в коде C99, но устарела в C++98 и удалена в C++11. Попытка изменить строку вызовет нарушение прав доступа, как показано в следующем примере:
wchar_t* str = L"hello"; str[2] = L'a'; // run-time error: access violation
Компилятор может вызвать ошибку при преобразовании строкового литерала в указатель неконстного символа при настройке /Zc:strictStrings параметра компилятора (отключить преобразование строковых литеральных типов). Мы рекомендуем использовать его для переносимого кода, соответствующего стандартам. Кроме того, рекомендуется использовать auto ключевое слово для объявления строковых инициализированных указателей, так как он разрешает правильный (const) тип. В следующем примере кода перехватывается во время компиляции попытка записать в строковый литерал:
auto str = L"hello"; str[2] = L'a'; // C3892: you cannot assign to a variable that is const.
В некоторых случаях идентичные строковые литералы могут быть объединены в пул для экономии места в исполняемом файле. При объединении строковых литералов в пулы компилятор делает так, что все ссылки на определенный строковый литерал указывают на одну и ту же область в памяти, вместо того чтобы каждая ссылка указывала на отдельный экземпляр строкового литерала. Чтобы включить пул строк, используйте параметр компилятора /GF .
Раздел , посвященный майкрософт , заканчивается здесь.
Сцепление смежных строковых литералов
Все смежные расширенные и узкие строковые литералы соединяются. Данное объявление:
char str[] = "12" "34";
идентично следующему объявлению:
char atr[] = "1234";
и следующему объявлению:
char atr[] = "12\ 34";
Использование внедренных шестнадцатеричных escape-кодов для задания строковых литералов может привести к непредвиденным результатам. В следующем примере выполняется попытка создать строковый литерал, содержащий символ ASCII 5, за которым следуют символы f, i, v и e:
"\x05five"
Фактический результат (шестнадцатеричное значение 5F) является кодом ASCII для символа подчеркивания, за которым следуют символы i, v и e. Чтобы получить правильный результат, можно использовать одну из следующих escape-последовательностей:
"\005five" // Use octal literal. "\x05" "five" // Use string splicing.
std::string литералы (и связанные std::u8string std::u16string , и std::u32string ) можно объединить с оператором + , определенным для basic_string типов. Эти литералы также можно соединить аналогично смежным строковым литералам. В обоих случаях кодировка строки и суффикс должны совпадать:
auto x1 = "hello" " " " world"; // OK auto x2 = U"hello" " " L"world"; // C2308: disagree on prefix auto x3 = u8"hello" " "s u8"world"z; // C3688, disagree on suffixes
Строковые литералы с универсальными именами символов
Машинные (не являющиеся необработанными) строковые литералы могут использовать универсальные имена символов для представления любого символа, при условии что универсальные имена можно кодировать как один или несколько символов в строковом типе. Например, универсальное имя символа, представляющее расширенный символ, не может быть закодировано в узкой строке с помощью кодовой страницы ANSI, но его можно закодировать в узких строках в некоторых многобайтовых кодовых страницах или в строках UTF-8 или в широкой строке. В C++11 поддержка Юникода расширяется типами char16_t* строк, char32_t* а C++20 расширяет его до char8_t типа:
// ASCII smiling face const char* s1 = ":-)"; // UTF-16 (on Windows) encoded WINKING FACE (U+1F609) const wchar_t* s2 = L" = \U0001F609 is ;-)"; // UTF-8 encoded SMILING FACE WITH HALO (U+1F607) const char* s3a = u8" = \U0001F607 is O:-)"; // Before C++20 const char8_t* s3b = u8" = \U0001F607 is O:-)"; // C++20 // UTF-16 encoded SMILING FACE WITH OPEN MOUTH (U+1F603) const char16_t* s4 = u" = \U0001F603 is :-D"; // UTF-32 encoded SMILING FACE WITH SUNGLASSES (U+1F60E) const char32_t* s5 = U" = \U0001F60E is B-)";
Какой тип данных используется в языке с для обозначения символьных строк
Каждая переменная имеет определенный тип. И этот тип определяет, какие значения может иметь переменная, какие операции с ней можно производить и сколько байт в памяти она будет занимать. В языке C++ определены следующие базовые типы данных: логический тип bool , целочисленные типы, типа чисел с плавающей точкой, символьные типы. Рассмотрим эти группы по отдельности.
Логический тип
Логический тип bool может хранить одно из двух значений: true (истинно, верно) и false (неверно, ложно). Например, определим пару переменных данного типа и выведем их значения на консоль:
#include int main() < bool isAlive ; bool isDead ; std::cout
При выводе значения типа bool преобразуются в 1 (если true) и 0 (если false). Как правило, данный тип применяется преимущество в условных выражениях, которые будут далее рассмотрены.
Значение по умолчанию для переменных этого типа - false .
Целочисленные типы
Целые числа в языке C++ представлены следующими типами:
- signed char : представляет один символ. Занимает в памяти 1 байт (8 бит). Может хранить любой значение из диапазона от -128 до 127
- unsigned char : представляет один символ. Занимает в памяти 1 байт (8 бит). Может хранить любой значение из диапазона от 0 до 255
- char : представляет один символ в кодировке ASCII. Занимает в памяти 1 байт (8 бит). Может хранить любое значение из диапазона от -128 до 127, либо от 0 до 255 Несмотря на то, что данный тип представляет тот же диапазон значений, что и вышеописанный тип signed char , но они не эквивалентны. Тип char предназначен для хранения числового кода символа и в реальности может представлять как signed byte , так и unsigned byte в зависимости от конкретного компилятора.
- short : представляет целое число в диапазоне от –32768 до 32767. Занимает в памяти 2 байта (16 бит). Данный тип также имеет псевдонимы short int , signed short int , signed short .
- unsigned short : представляет целое число в диапазоне от 0 до 65535. Занимает в памяти 2 байта (16 бит). Данный тип также имеет синоним unsigned short int .
- int : представляет целое число. В зависимости от архитектуры процессора может занимать 2 байта (16 бит) или 4 байта (32 бита). Диапазон предельных значений соответственно также может варьироваться от –32768 до 32767 (при 2 байтах) или от −2 147 483 648 до 2 147 483 647 (при 4 байтах). Но в любом случае размер должен быть больше или равен размеру типа short и меньше или равен размеру типа long Данный тип имеет псевдонимы signed int и signed .
- unsigned int : представляет положительное целое число. В зависимости от архитектуры процессора может занимать 2 байта (16 бит) или 4 байта (32 бита), и из-за этого диапазон предельных значений может меняться: от 0 до 65535 (для 2 байт), либо от 0 до 4 294 967 295 (для 4 байт). Имеет псевдоним unsigned
- long : в зависимости от архитектуры может занимать 4 или 8 байт и представляет целое число в диапазоне от −2 147 483 648 до 2 147 483 647 (при 4 байтах) или от −9 223 372 036 854 775 808 до +9 223 372 036 854 775 807 (при 8 байтах). Занимает в памяти 4 байта (32 бита) или. Имеет псевдонимы long int , signed long int и signed long
- unsigned long : представляет целое число в диапазоне от 0 до 4 294 967 295. Занимает в памяти 4 байта (32 бита). Имеет синоним unsigned long int .
- long long : представляет целое число в диапазоне от −9 223 372 036 854 775 808 до +9 223 372 036 854 775 807. Занимает в памяти 8 байт (64 бита). Имеет псевдонимы long long int , signed long long int и signed long long .
- unsigned long long : представляет целое число в диапазоне от 0 до 18 446 744 073 709 551 615. Занимает в памяти, как правило, 8 байт (64 бита). Имеет псевдоним unsigned long long int .
Для представления чисел в С++ применятся целочисленные литералы со знаком или без, типа -10 или 10. Например, определим ряд переменных целочисленных типов и выведем их значения на консоль:
#include int main() < signed char num1< -64 >; unsigned char num2< 64 >; short num3< -88 >; unsigned short num4< 88 >; int num5< -1024 >; unsigned int num6< 1024 >; long num7< -2048 >; unsigned long num8< 2048 >; long long num9< -4096 >; unsigned long long num10< 4096 >; std::cout u или U. Литералы типовlongиlong longимеют суффиксы L/l и LL/ll соответственно:#include int main() < unsigned int num6< 1024U >; // U - unsigned int long num7< -2048L >; // L - long unsigned long num8< 2048UL >; // UL - unsigned long long long num9< -4096LL >; // LL - long long unsigned long long num10< 4096ULL >;// ULL - unsigned long long std::cout #include int main() < int num< 1'234'567'890 >; std::coutРазличные системы исчисления
По умолчанию все стандартные целочисленные литералы представляют числа в привычной нам десятичной системе. Однако C++ также позволяет использовать и числа в других системах исчисления.
Чтобы указать, что число - шестнадцатеричное, перед числом указывается префикс 0x или 0X . Например:
int num1< 0x1A>; // 26 - в десятичной int num2< 0xFF >; // 255 - в десятичной int num3< 0xFFFFFF >; //16777215 - в десятичнойЧтобы указать, что число - восьмеричное, перед числом указывается ноль 0 . Например:
int num1< 034>; // 26 - в десятичной int num2< 0377 >; // 255 - в десятичнойБинарные литералы предваряются префиксом 0b или 0B :
int num1< 0b11010>; // 26 - в десятичной int num2< 0b11111111 >; // 255 - в десятичнойВсе эти типы литералов также поддерживают суффиксы U/L/LL :
unsigned int num1< 0b11010U>; // 26 - в десятичной long num2< 0377L >; // 255 - в десятичной unsigned long num3< 0xFFFFFFULL >; //16777215 - в десятичнойЧисла с плавающей точкой
Для хранения дробных чисел в C++ применяются числа с плавающей точкой. Число с плавающей точкой состоит из двух частей: мантиссы и показателя степени . Оба могут быть как положительными, так и отрицательными. Величина числа – это мантисса, умноженная на десять в степени экспоненты.
Например, число 365 может быть записано в виде числа с плавающей точкой следующим образом:
3.650000E02В качестве разделителя целой и дробной частей используется символ точки. Мантисса здесь имеет семь десятичных цифр - 3.650000 , показатель степени - две цифры 02 . Буква E означает экспоненту, после нее указывается показатель степени (степени десяти), на которую умножается часть 3.650000 (мантисса), чтобы получить требуемое значение. То есть, чтобы вернуться к обычному десятичному представлению, нужно выполнить следующую операцию:
3.650000 × 102 = 365Другой пример - возьмем небольшое число:
-3.650000E-03В данном случае мы имеем дело с числом –3.65 × 10 -3 , что равно –0.00365 . Здесь мы видим, что в зависимости от значения показателя степени десятичная точка "плавает". Собственно поэтому их и называют числами с плавающей точкой.
Однако хотя такая запись позволяет определить очень большой диапазон чисел, не все эти числа могут быть представлены с полной точностью; числа с плавающей запятой в целом являются приблизительными представления точного числа. Например, число 1254311179 выглядело бы так: 1.254311E09 . Однако если перейти к десятичной записи, то это будет 1254311000 . А это не то же самое, что и 1254311179 , поскольку мы потеряли три младших разряда.
В языке C++ есть три типа для представления чисел с плавающей точкой:
- float : представляет вещественное число одинарной точности с плавающей точкой в диапазоне +/- 3.4E-38 до 3.4E+38. В памяти занимает 4 байта (32 бита)
- double : представляет вещественное число двойной точности с плавающей точкой в диапазоне +/- 1.7E-308 до 1.7E+308. В памяти занимает 8 байт (64 бита)
- long double : представляет вещественное число двойной точности с плавающей точкой не менее 8 байт (64 бит). В зависимости от размера занимаемой памяти может отличаться диапазон допустимых значений.
В своем внутреннем бинарном представлении каждое число с плавающей запятой состоит из одного бита знака, за которым следует фиксированное количество битов для показателя степени и набор битов для хранения мантиссы. В числах float 1 бит предназначен для хранения знака, 8 бит для экспоненты и 23 для мантиссы, что в сумме дает 32 бита. Мантисса позволяет определить точность числа в виде 7 десятичных знаков.
В числах double : 1 знаковый бит, 11 бит для экспоненты и 52 бит для мантиссы, то есть в сумме 64 бита. 52-разрядная мантисса позволяет определить точность до 16 десятичных знаков.
Для типа long double расклад зависит от конкретного компилятора и реализации этого типа данных. Большинство компиляторов предоставляют точность до 18 - 19 десятичных знаков (64-битная мантисса), в других же (как например, в Microsoft Visual C++) long double аналогичен типу double .
В C++ литералы чисел с плавающими точками представлены дробными числами, которые в качестве разделителя целой и дробной частей применяют точку:
double num ;
Даже если переменной присваивается целое число, чтобы показать, что мы присваиваем число с плавающей точкой, применяется точка:
double num1< 1 >; // 1 - целочисленный литерал double num2< 1. >; //1. - литерал числа с плавающей точкой
Так, здесь число 1. представляет литерал числа с плавающей точкой, и в принципе аналогичен 1.0 .
По умолчанию все такие числа с точкой расцениваются как числа типа double. Чтобы показать, что число представляет другой тип, для float применяется суффикс f / F , а для long double - l / L :
float num1< 10.56f >; // float long double num2< 10.56l >; // long double
В качестве альтернативы также можно применять экспоненциальную запись:
double num1< 5E3 >; // 5E3 = 5000.0 double num2< 2.5e-3 >; // 2.5e-3 = 0.0025
Размеры типов данных
При перечислении типов данных указывался размер, который он занимает в памяти. Но стандарт языка устанавливает лишь минимальные значения, которые должны быть. Например, для типов int и short минимальное значение - 16 бит, для типа long - 32 бита, для типа long double - 64 разряда. При этом размер типа long должен быть не меньше размера типа int, а размер типа int - не меньше размера типа short, а размер типа long double должен быть не меньше double . А разработчики компиляторов могут выбирать предельные размеры для типов самостоятельно, исходя из аппаратных возможностей компьютера.
К примеру, компилятор g++ Windows для long double использует 16 байт. А компилятор в Visual Studio, который также работает под Windows, и clang++ под Windows для long double используют 8 байт. То есть даже в рамках одной платформы разные компиляторы могут по разному подходить к размерам некоторых типов данных. Но в целом используются те размеры, которые указаны выше при описании типов данных.
Однако бывают ситуации, когда необходимо точно знать размер определенного типа. И для этого в С++ есть оператор sizeof() , который возвращает размер памяти в байтах, которую занимает переменная:
#include int main() < long double number ; std::coutsizeof(number) = 16Символьные типы
В C++ есть следующие символьные типы данных:
- char : представляет один символ в кодировке ASCII. Занимает в памяти 1 байт (8 бит). Может хранить любое значение из диапазона от -128 до 127, либо от 0 до 255
- wchar_t : представляет расширенный символ. На Windows занимает в памяти 2 байта (16 бит), на Linux - 4 байта (32 бита). Может хранить любой значение из диапазона от 0 до 65 535 (при 2 байтах), либо от 0 до 4 294 967 295 (для 4 байт)
- char8_t : представляет один символ в кодировке Unicode. Занимает в памяти 1 байт. Может хранить любой значение из диапазона от 0 до 256
- char16_t : представляет один символ в кодировке Unicode. Занимает в памяти 2 байта (16 бит). Может хранить любой значение из диапазона от 0 до 65 535
- char32_t : представляет один символ в кодировке Unicode. Занимает в памяти 4 байта (32 бита). Может хранить любой значение из диапазона от 0 до 4 294 967 295
char
Переменная типа char хранит числовой код одного символа и занимает один байт. Стандарт языка С++ не определяет кодировку символов, которая будет использоваться для символов char, поэтому производители компиляторов могут выбирать любую кодировку, но обычно это ASCII.
В качестве значения переменная типа char может принимать один символ в одинарных кавычках, либо числовой код символа:
#include int main() < char a1 ; char a2 ; std::cout
В данном случае переменные a1 и a2 будут иметь одно и то же значение, так как 65 - это числовой код символа "A" в таблице ASCII. При выводе на консоль с помощью cout по умолчанию отображается символ.
Кроме того, в C++ можно использовать специальные управляющие последовательности, которые предваряются слешем и которые интерпретируются особым образом. Например, "\n" представляет перевод строки, а "\t" - табуляцию.
Однако ASCII обычно подходит для наборов символов языков, которые используют латиницу. Но если необходимо работать с символами для нескольких языков одновременно или с символами языков, отличных от английского, 256-символьных кодов может быть недостаточно. И в этом случае применяется Unicode .
Unicode (Юникод) — это стандарт, который определяет набор символов и их кодовых точек, а также несколько различных кодировок для этих кодовых точек. Наиболее часто используемые кодировки: UTF-8, UTF-16 и UTF-32. Разница между ними заключается в том, как представлена кодовая точка символа; числовое же значение кода для любого символа остается одним и тем же в любой из кодировок. Основные отличия:
- UTF-8 представляет символ как последовательность переменной длины от одного до четырех байт. Набор символов ASCII появляется в UTF-8 как однобайтовые коды, которые имеют те же значения кодов, что и в ASCII. UTF-8 на сегодняшний день является самой популярной кодировкой Unicode.
- UTF-16 представляет символы как одно или два 16-битных значения.
- UTF-32 представляет все символы как 32-битные значения
В C++ есть четыре типа для хранения символов Unicode: wchar_t , char8_t , char16_t и char32_t ( char16_t и char32_t были добавлены в C+11, а char8_t - в C++20).
wchar_t
Тип wchar_t — это основной тип, предназначенный для наборов символов, размер которых выходит за пределы одного байта. Собственно отсюда и его название: wchar_t - wide (широкий) char. происходит от широкого символа, потому что этот символ «шире», чем обычный однобайтовый символ. Значения wchar_t определяются также как и символы char за тем исключением, что они предваряются символов "L":
wchar_t a1 ;
Также можно передать код символа
wchar_t a1 ;
Значение, заключенное в одинарные кавычки, представляет собой шестнадцатеричный код символа. Обратная косая черта указывает на начало управляющей последовательности, а x после обратной косой черты означает, что код шестнадцатеричный.
Стоит учитывать, что для вывода на консоль символов wchar_t следует использовать не std::cout , а поток std::wcout :
#include int main() < char h = 'H'; wchar_t i ; std::wcout
При этом поток std::wcout может работать как с char, так и с wchar_t. А поток std::cout для переменной wchar_t вместо символа будет выводить его числовой код.
Проблема с типом wchar_t заключается в том, что его размер сильно зависит от реализации и применяемой кодировки. Кодировка обычно соответствует предпочтительной кодировке целевой платформы. Так, для Windows wchar_t обычно имеет ширину 16 бит и кодируется с помощью UTF-16. Большинство других платформ устанавливают размер в 32 бита, а в качестве кодировки применяют UTF-32. С одной стороны, это позволяет больше соответствовать конкретной платформе. Но с другой стороны, затрудняет написание кода, переносимого на разные платформы. Поэтому в общем случае часто рекомендуется использовать типы char8_t , char16_t и char32_t . Значения этих типов предназначены для хранения символов в кодировке UTF-8, UTF-16 или UTF-32 соответственно, а их размеры одинаковы на всех распространенных платформах.
Для определения символов типов char8_t , char16_t и char32_t применяются соответственно префиксы u8, u и U:
char8_t c< u8'l' >; char16_t d< u'l' >; char32_t e< U'o' >;
Стоит отметить, что для вывода на консоль значений char8_t/char16_t/char32_t пока нет встроенных инструментов типа std:cout/std:wcout .
Спецификатор auto
Иногда бывает трудно определить тип выражения. В этом случае можно предоставить компилятору самому выводить тип объекта. И для этого применяется спецификатор auto . При этом если мы определяем переменную со спецификатором auto, эта переменная должна быть обязательно инициализирована каким-либо значением:
auto number = 5; // number имеет тип int auto sum ; // sum имеет тип double auto distance ; // distance имеет тип unsigned long
На основании присвоенного значения компилятор выведет тип переменной. Неинициализированные переменные со спецификатором auto не допускаются:

