JavaRush /Курсы /C++ SELF /Литералы и суффиксы в C++: числа, символы и escape

Литералы и суффиксы в C++: числа, символы и escape

C++ SELF
7 уровень , 5 лекция
Открыта

1. Литералы и их тип: значение прямо в коде

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

Литерал — это значение, записанное непосредственно в исходнике программы: число, символ, строка. Например, 10, 3.14, 'A', "Hello". И ключевая мысль этой лекции: литерал — это не просто “42”, а “42 определённого типа”, и от этого типа зависит, как будут работать выражения и преобразования.

Мини‑пример, чтобы почувствовать идею «тип важен, даже если вы его не видите»:

#include <iostream>

int main() {
    std::cout << (5 / 2) << '\n';      // 2
    std::cout << (5 / 2.0) << '\n';    // 2.5
}

Здесь меняется всего один символ (.0), но смысл вычислений другой — и это как раз про литералы.

2. Числовые литералы: без суффиксов и с суффиксами

Числа без суффиксов: что компилятор “подразумевает”

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

  • целое без точки похоже на int;
  • число с точкой похоже на double.

Это не вся правда стандарта, но для уверенного старта — отличная рабочая карта.

Вещественный литерал по умолчанию — это double, поэтому 3.14 обычно воспринимается как double. Целочисленный литерал 42 чаще всего ведёт себя как int. Если вы хотите управлять типом явно, вы либо добавляете суффикс, либо делаете приведение через static_cast.

Проверим «кто есть кто» в простом выводе (без typeid, чтобы не забегать вперёд):

#include <iostream>

int main() {
    int a = 42;        // 42 — целое, хорошо ложится в int
    double b = 3.14;   // 3.14 — вещественное, по умолчанию double

    std::cout << a << '\n'; // 42
    std::cout << b << '\n'; // 3.14
}

Пока всё спокойно. Но как только появляются границы диапазонов или смешивание signed/unsigned (это будет отдельной темой позже), становится важно уметь «подсказывать» компилятору ваше намерение.

Суффиксы для целых: u и ll

Суффикс у литерала — это как наклейка “осторожно, хрупкое” на коробке: содержимое то же, но окружающие (компилятор и читатели кода) начинают вести себя аккуратнее. Суффиксы помогают избежать неожиданностей и лишних неявных преобразований. В этой лекции нас интересуют два самых жизненных: u (unsigned) и ll (long long).

42u: литерал как unsigned

Когда вы пишете 42u, вы прямо говорите: «это беззнаковое число». Самый простой прикладной смысл: вы подчёркиваете, что значение не предполагает минуса. Часто это счётчики, количества, размеры. Но важно помнить: беззнаковость — не “число добрее”, а “число по‑другому устроено”.

#include <iostream>

int main() {
    unsigned attempts = 3u;
    std::cout << attempts << '\n'; // 3
}

42ll: литерал как long long

Суффикс ll полезен, когда вы точно понимаете, что значение может быть большим, и хотите сразу это выразить. Например, если вы работаете с деньгами в копейках/центах или с большим счётчиком.

#include <iostream>

int main() {
    long long big = 42ll;
    std::cout << big << '\n'; // 42
}

Мини‑таблица: что мы сегодня считаем важным:

Запись Пример Идея
целый литерал
42
«обычно int» (в рамках нашей модели)
unsigned‑литерал
42u
«точно без минуса»
long long‑литерал
42ll
«хочу большой целый тип»
вещественный литерал
3.14
по умолчанию double

Вещественные литералы: ловушка “я же положил в double!”

Вещественные литералы выглядят дружелюбно: точка есть — значит “дробь”. И правда: 3.14 — это double. Но ловушка обычно не в том, какой тип у самого литерала, а в том, как он участвует в выражениях и преобразованиях.

Классический пример:

#include <iostream>

int main() {
    int a = 5;
    int b = 2;

    double r = a / b;
    std::cout << r << '\n'; // 2
}

Почему 2, а не 2.5? Потому что a / b вычисляется как int / int, то есть целочисленно, и только после этого результат (2) превращается в double.

Исправление — сделать хотя бы один операнд вещественным литералом:

#include <iostream>

int main() {
    int a = 5;
    int b = 2;

    double r = a / 2.0;
    std::cout << r << '\n'; // 2.5
}

Да, 2.0 — это тоже литерал, и он меняет тип вычисления.

3. Символ и строка: 'A' против "A"

Самое частое «новичковое» удивление: почему 'A' и "A" выглядят почти одинаково, но ведут себя по‑разному? Потому что это принципиально разные вещи.

  • Символьный литерал 'A' — это один символ (тип char).
  • Строковый литерал "A" — это строка (на практике — массив символов, но на старте достаточно думать “строка”).

Разница особенно заметна, когда вы пытаетесь хранить значения в переменных правильного типа:

#include <iostream>
#include <string>

int main() {
    char c = 'A';
    std::string s = "A";

    std::cout << c << '\n'; // A
    std::cout << s << '\n'; // A
}

Снаружи одинаково, но по смыслу — нет. Символ — это один “кирпичик текста”. Строка — это “целое слово/фраза”, даже если там всего один кирпичик.

Практический совет: если вы храните одну букву (разделитель, знак операции, код ответа), берите char и пишите '...'. Если вы храните текст (пусть даже короткий), берите std::string и пишите "...".

4. Escape‑последовательности: как записывать «неудобные» символы

Иногда вам нужно записать символ, который сложно или невозможно “нарисовать” прямо в коде: перевод строки, табуляцию, кавычку внутри кавычек, обратный слэш. Для этого и существуют escape‑последовательности: запись вида '\n', "\t", "\"" и так далее.

\n и \t в выводе

Начнём с самых нужных в консольных программах: \n и \t.

#include <iostream>

int main() {
    std::cout << "A\tB\tC\n";   // A    B    C
    std::cout << "Line1\nLine2\n";
    // Line1
    // Line2
}

Здесь \t — табуляция (удобно для колонок), а \n — перевод строки.

Важно увидеть мысль: escape‑последовательности работают и в символьных, и в строковых литералах, но результат разный по типу.

  • '\n' — это один символ (char).
  • "\n" — это строка (хотя визуально кажется “один символ”).

Поэтому если функция или переменная ожидает char, давайте '\n'. Если ожидает строку — "\n".

Кавычки и обратный слэш: \", \', \\

Вывести кавычку внутри строки — это классический мини‑квест. Строка начинается и заканчивается "...", значит внутренняя кавычка должна быть экранирована: \". С обратным слэшем то же: он сам “служебный”, поэтому чтобы получить символ \, нужно написать \\.

Практичные примеры:

#include <iostream>

int main() {
    std::cout << "He said: \"Hi\".\n";         // He said: "Hi".
    std::cout << "Path: C:\\temp\\file.txt\n"; // Path: C:\temp\file.txt
}

Теперь — то же самое на уровне char, когда вам нужен одиночный символ:

#include <iostream>

int main() {
    char quote = '\"';
    char slash = '\\';

    std::cout << quote << '\n'; // "
    std::cout << slash << '\n'; // \
}

Обратите внимание: '\'' (одинарная кавычка как символ) тоже существует, но на первых шагах чаще всего вам нужна именно двойная кавычка в строке (\") или слэш (\\).

5. Практика: мини‑приложение MiniCheck

Давайте соберём маленькую консольную программу “MiniCheck”: она спрашивает цену и количество, считает итог и печатает «чек». Это хороший полигон для литералов: здесь есть числа, символы‑разделители, строки с кавычками и табуляцией, и константы.

Шапка и аккуратный вывод

Обратите внимание на '\n' и '\t':

#include <iostream>
#include <string>

int main() {
    const std::string shop_name = "MiniCheck";
    std::cout << "=== " << shop_name << " ===\n";    // === MiniCheck ===
    std::cout << "item\tprice\tqty\n";              // item    price   qty
}

Ввод и расчёт в целых типах

Будем хранить цену в “центах”, чтобы не упираться в дроби (про точность double будет отдельный день курса). Для “центов” подходит целый тип, а чтобы намекнуть на “большие числа”, используем long long и суффикс ll для нулевой инициализации. Для количества — unsigned и суффикс u.

#include <iostream>
#include <string>

int main() {
    std::string item;
    long long price_cents = 0ll;
    unsigned qty = 0u;

    std::cout << "Item name: ";
    std::cin >> item;

    std::cout << "Price (cents): ";
    std::cin >> price_cents;

    std::cout << "Quantity: ";
    std::cin >> qty;

    long long total = price_cents * static_cast<long long>(qty);
    std::cout << "Total cents: " << total << '\n';
}

Здесь мы сделали две важные вещи:

  • Во‑первых, показали суффиксы 0ll и 0u как “подсказку по типу”.
  • Во‑вторых, использовали static_cast<long long>(qty), чтобы перемножение точно происходило в большом целочисленном типе (мы не прячем намерение).

Кавычки вокруг текста: \" в реальной задаче

Добавим “красивую” строку с кавычками вокруг названия товара:

#include <iostream>
#include <string>

int main() {
    std::string item;
    long long price_cents = 0ll;
    unsigned qty = 0u;

    std::cin >> item >> price_cents >> qty;

    std::cout << "You bought: \"" << item << "\"\n"; // You bought: "cola"
}

\" здесь — escape‑последовательность, чтобы кавычки стали частью выводимого текста, а не завершили строковый литерал.

Как компилятор «сворачивает» escape в строке

Когда вы пишете "A\nB", в исходнике это выглядит как символы A, \, n, B. Но компилятор превращает \n в один специальный символ “перевод строки”. Это полезно представлять, чтобы не удивляться, почему длина строки “не равна количеству видимых значков в коде”.

flowchart LR
    A["Исходник: \"A\\nB\""] --> B["Компилятор читает escape"]
    B --> C["В памяти: 'A' затем '\\n' затем 'B'"]
    C --> D["При выводе: A (новая строка) B"]

Та же идея работает для \\, \", \t и других.

6. Типичные ошибки

Ошибка №1: путаница 'A' и "A".
Новички часто воспринимают оба варианта как “букву A”, но в C++ это разные типы и разные сущности. 'A' — один символ char, "A" — строка. Если вы пытаетесь хранить одну букву в std::string, это не страшно, но если вы пытаетесь хранить строку в char, программа либо не скомпилируется, либо начнёт вести себя странно.

Ошибка №2: путаница '\n' и "\n".
Оба варианта «про перевод строки», но первый — один символ, второй — строка. В выводе через std::cout оба обычно работают, но как только вы начнёте передавать значения в функции или складывать строки, разница станет принципиальной. Хорошая привычка: для одиночных символов использовать одинарные кавычки, для текста — двойные.

Ошибка №3: забыли экранировать кавычку или обратный слэш.
Строка "He said: "Hi"" не работает, потому что внутренняя кавычка завершает строковый литерал раньше времени. Нужно писать \". С обратным слэшем то же: чтобы вывести \, пишите \\.

Ошибка №4: использование суффикса “наугад”, чтобы компилятор отстал.
Иногда хочется поставить u или ll просто потому, что “так прошло”. Это плохая стратегия: суффикс — часть смысла. Если вы пишете u, вы реально соглашаетесь на беззнаковую арифметику. Если пишете ll, вы заявляете, что хотите работать в long long. Сначала объясните это себе словами (“может ли быть минус?”, “может ли число быть большим?”), и только потом закрепляйте суффиксом.

Ошибка №5: ожидание, что 3.14 — это “просто число”, а не double.
В выражениях тип литерала влияет на тип вычислений. Если вы случайно оставили все операнды целыми, то a / b будет целочисленным, даже если результат кладёте в double. Проверяйте, есть ли в формуле хотя бы один вещественный литерал (например, 2.0) или осознанное приведение типа.

1
Задача
C++ SELF, 7 уровень, 5 лекция
Недоступна
Трудные строки
Трудные строки
1
Задача
C++ SELF, 7 уровень, 5 лекция
Недоступна
Паспорт символа
Паспорт символа
1
Задача
C++ SELF, 7 уровень, 5 лекция
Недоступна
Два деления
Два деления
1
Задача
C++ SELF, 7 уровень, 5 лекция
Недоступна
Чек в центах
Чек в центах
1
Опрос
Инициализация, const/constexpr, 7 уровень, 5 лекция
Недоступен
Инициализация, const/constexpr
Инициализация, const/constexpr
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Alex Blizz Уровень 17
25 апреля 2026
Зачем писать 42ll если и так уже указано long long ?

long long big = 42ll;
Андрей Уровень 16
4 мая 2026
Для таких случаев, когда в правой части может произойти переполнение типа при вычислении, до присвоения. Так будет переполнение или ошибка компиляции, signed INT_MAX + 1:

long long big = 2147483647 + 1; // выход за пределы диапазона
А так всё нормально, (u) или (LL):

long long big = 2147483647u + 1; // 2147483648
long long big = 2147483647LL + 1; // 2147483648