Урок №36. Литералы и магические числа

  Юрий  | 

    | 

  Обновл. 4 Апр 2019  | 

 14196

 ǀ   8 

В C++ есть два вида констант: литеральные и символьные. В этом уроке мы рассмотрим литеральные константы.

Литеральные константы

Литеральные константы (или просто «литералы») — это значения, которые вставляются непосредственно в код. Поскольку они являются константами, то их значения изменить нельзя. Например:

С литералами bool и int всё понятно, а вот с литералами типа с плавающей точкой есть два способы объявления:

Во втором способе объявления, число после экспонента может быть и отрицательным:

Числовые литералы могут иметь суффиксы, которые определяют их типы. Эти суффиксы не являются обязательными, так как компилятор понимает из контекста, константу какого типа данных вы хотите использовать.

Тип данных Суффикс Значение
int u или U unsigned int
int l или L long
int ul, uL, Ul, UL, lu, lU, Lu или LU unsigned long
int ll или LL long long
int ull, uLL, Ull, ULL, llu, llU, LLu или LLU unsigned long long
double f или F float
double l или L long double

Суффиксы есть даже для целочисленных типов (но они почти не используются):

По умолчанию литеральные константы типа с плавающей точкой являются типа double. Для конвертации литеральных констант в тип float, можно использовать суффикс f или F:

C++ также поддерживает литералы типов string и char:

Литералы хорошо использовать в коде до тех пор, пока их значения понятны и однозначны. Это выполнение операций присваивания, математических операций или вывода текста в консоль.

Литералы в восьмеричной и шестнадцатеричной системах счисления


В повседневной жизни мы используем десятичную систему счисления, которая состоит из 10 цифр: 0, 1, 2, 3, 4, 5, 6, 7, 8 и 9. По умолчанию C++ использует десятичную систему счисления для чисел в программах:

В двоичной (бинарной) системе счисления всего 2 цифры: 0 и 1. Значения: 0, 1, 10, 11, 100, 101, 110, 111 и т. д.

Есть ещё две другие системы счисления: восьмеричная и шестнадцатеричная.

Восьмеричная система счисления состоит из 8 цифр: 0, 1, 2, 3, 4, 5, 6 и 7. Значения: 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12 и т. д. (примечание: цифр 8 и 9 — нет, так что сразу перескакиваем от 7 к 10).

Десятичная система счисления 0 1 2 3 4 5 6 7 8 9 10 11
Восьмеричная система счисления 0 1 2 3 4 5 6 7 10 11 12 13

Для использования литерала из восьмеричной системы счисления, используйте префикс 0 (ноль):

Результат выполнения программы выше:

10

Почему 10 вместо 12? Потому что std::cout выводит числа в десятичной системе счисления, а 12 в восьмеричной системе = 10 в десятичной.

Восьмеричная система счисления используется крайне редко.

Шестнадцатеричная система счисления состоит из 16 символов: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, А, В, С, D, Е, F.

Десятичная система 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Шестнадцатеричная система 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11

Для использования литерала из шестнадцатеричной системы счисления, используйте префикс 0x:

Результат выполнения программы выше:

15

Поскольку в этой системе 16 символов, то одна шестнадцатеричная цифра занимает 4 бита. Следовательно, две шестнадцатеричные цифры занимают 1 байт.

Рассмотрим 32-битовое целое число из двоичной системы счисления: 0011 1010 0111 1111 1001 1000 0010 0110. Из-за длины и повторения цифр его сложно прочесть. В шестнадцатеричной системе счисления это же значение будет выглядеть следующим образом: 3A7F 9826. Такой удобный/сжатый формат является преимуществом шестнадцатеричной системы счисления. Поэтому шестнадцатеричные значения часто используются для представления адресов памяти или необработанных значений в памяти.

До C++14 использовать литерал из двоичной системы счисления было невозможно. Тем не менее, шестнадцатеричная система счисления может нам в этом помочь:

Бинарные литералы и разделитель цифр в C++14

В C++14 мы можем использовать бинарные (двоичные) литералы, добавляя префикс 0b:

Поскольку длинные литералы читать трудно, то в C++14 добавили возможность использовать одинарную кавычку в качестве разделителя цифр:

Если ваш компилятор не поддерживает C++14, то использовать бинарные литералы и разделитель цифр вы не сможете — компилятор выдаст ошибку.

Магические числа. Что с ними не так?


Рассмотрим следующий фрагмент кода:

В примере выше число 30 является магическим числом. Магическое число — это хорошо закодированный литерал (обычно, число) в строчке кода, который не имеет никакого контекста. Что это за 30, что оно означает/обозначает? Хотя из примера выше можно догадаться, что число 30 обозначает максимальное количество учеников, находящихся в одном кабинете — в большинстве случаев, это не всегда будет столь очевидным и понятным. В более сложных программах контекст подобных чисел разгадать намного сложнее (если только не будет соответствующих комментариев).

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

Рассмотрим следующий фрагмент кода:

Чтобы обновить число учеников в кабинете, нам нужно изменить значение константы с 30 на 36. Но что делать с вызовом функции setMax(30)? Аргумент 30 и константа 30 в коде выше является одним и тем же, верно? Если да, то нам нужно будет обновить это значение. Если нет, то нам не следует вообще трогать этот вызов функции. Если же проводить автоматический глобальный поиск и замену числа 30, то можно ненароком изменить и аргумент функции setMax(), в то время, когда его вообще не следовало бы трогать. Поэтому вам придётся просмотреть весь код «вручную», в поисках числа 30, а затем, в каждом конкретном случае, определить: изменить ли 30 на 36 или нет. Это может занять очень много времени + вероятность возникновения новых ошибок повышается в разы.

К счастью, есть лучший вариант — использовать символьные константы. О них мы поговорим в следующем уроке.

Правило: Старайтесь сократить к минимуму использование магических чисел в ваших программах.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (109 оценок, среднее: 4,81 из 5)
Загрузка...

Комментариев: 8

  1. Аватар alexk:

    Чтобы можно было применять цифровые разделители, т.е. записывать литералы в виде:

    необходимо установить ключ компиляции:

    g++ -Wall -std=c++1y ...

    Это справедливо для:
    g++ (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609

  2. Аватар Константин:

    И ещё не могу эту последовательность разгадать после 0x: 01; 02; 04; 08; 10; 20; 40; 80; FF; особенно здесь B3; F770;

    1. Аватар Константин:

      Уррр-ааа!!! Разгадал! А это значение в этот тип не помещается:

      Нужен long long

  3. Аватар Константин:

    Юра, пардон, но я не понял для чего была создана int bin, и, тем более, для чего мы в ней столько раз подряд значения меняли?

  4. Аватар Иван:

    До меня дошло. Невнимательно прочитал определение литерла. Спасибо!

    1. Юрий Юрий:

      Пожалуйста 🙂

  5. Аватар Иван:

    "Литеральные константы (обычно просто литералы) – это значения, которые вставляются непосредственно в код. Они являются константами, так как изменить их значения нельзя. "

    А почему мы не можем изменить их значения? Можно же просто присвоить другое значение этому литералу, судя по примерам в начале урока

    1. Юрий Юрий:

      Судя по какому примеру? Вы можете присвоить числу 4 (четыре) значение 5 (пять), чтобы 4 был равен 5? Или можно сделать, чтобы true был false, а false = true? Есть литералы, а есть переменные. Значение переменных можно изменять, значения литералов — нет.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *