4.13 – Литералы
В программировании константа – это фиксированное значение, которое нельзя изменять. В C++ есть два типа констант: литеральные константы и символьные константы. В этом уроке мы рассмотрим литеральные константы, а в следующем – символьные константы.
Литеральные константы (обычно называемые просто литералами) – это значения, вставленные непосредственно в код. Например:
Они являются константами, потому что их значения не могут быть изменены динамически (сначала вы должны изменить их, а затем перекомпилировать программу, чтобы изменение вступило в силу).
Так же, как у объектов есть тип, тип есть и у всех литералов. Тип литерала предполагается из значения и формата самого литерала.
Значение литерала | Примеры | Тип по умолчанию |
---|---|---|
целое число | 5, 0, -3 | int |
логическое значение | true , false | bool |
число с плавающей запятой | 3.4, -2.2 | double (не float ) |
символ | ' a ' | char |
строка в стиле C | " Hello, world! " | const char[14] |
Суффиксы литералов
Если тип литерала по умолчанию не соответствует необходимому, вы можете изменить тип литерала, добавив суффикс:
Тип данных | Суффикс | Назначение |
---|---|---|
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 ):
Начинающие программисты часто не понимают, почему следующий код работает не так, как ожидалось:
Поскольку у 4.1 нет суффикса, он рассматривается как литерал типа double , а не как литерал типа float . Когда C++ определяет тип литерала, ему всё равно, что вы делаете с литералом (например, в данном случае используете его для инициализации переменной float ). Следовательно, 4.1 необходимо преобразовать из double в float , прежде чем его можно будет присвоить переменной f , и это может привести к потере точности.
Литералы можно использовать в коде C++, если их значения понятны. Чаще всего это происходит при использовании для инициализации или присвоения значения переменной, выполнения математических операций или вывода текста на экран.
Строковые литералы
В уроке «4.11 – Символы» мы определили строку как набор последовательных символов. C++ поддерживает строковые литералы:
Строковые литералы в C++ по историческим причинам обрабатываются очень странно. На данный момент можно использовать строковые литералы для печати текста с помощью std::cout , но не пытайтесь назначать их переменным или передавать их функциям – это либо не сработает, либо будет работать не так, как вы ожидаете. О строках в стиле C (и о том, как обойти все эти странные проблемы) мы поговорим подробнее в будущих уроках.
Экспоненциальная запись для числовых литералов с плавающей запятой
Есть два разных способа объявить литералы с плавающей точкой:
Во второй форме число после экспоненты может быть отрицательным:
Литералы в восьмеричной и шестнадцатеричной системах счисления
В повседневной жизни мы считаем, используя числа в десятичной системе счисления, где каждая цифра может быть 0, 1, 2, 3, 4, 5, 6, 7, 8 или 9. Десятичная система счисления число также называется «с основанием 10», потому что в ней возможно использование 10 цифр (от 0 до 9). В этой системе мы считаем так: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,… По умолчанию, числа в программах на C++ считаются десятичными.
В двоичной системе счисления всего 2 цифры: 0 и 1, поэтому она называется «с основанием 2». В двоичном формате мы считаем так: 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, а не 12? Потому что числа печатаются в десятичном формате, а 12 в восьмеричном формате = 10 десятичном формате.
Восьмеричная система счисления практически не используется, и мы рекомендуем вам ее избегать.
Шестнадцатеричная система счисления — с основанием 16. В шестнадцатеричной системе мы считаем так: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12,…
Десятичная система | 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 .
Эта программа печатает:
Поскольку для одной шестнадцатеричной цифры возможны 16 различных значений, мы можем сказать, что одна шестнадцатеричная цифра использует 4 бита. Следовательно, пара шестнадцатеричных цифр может использоваться для точного представления полного байта.
Рассмотрим 32-битное целое число со значением 0011 1010 0111 1111 1001 1000 0010 0110 . Из-за длины и повторяемости цифр его нелегко прочитать. В шестнадцатеричном формате это же значение будет выглядеть так: 3A7F 9826 . Это делает шестнадцатеричные значения полезными как краткий способ представления значения в памяти. По этой причине шестнадцатеричные значения часто используются для представления адресов или необработанных значений в памяти.
До C++14 не было возможности назначить литерал в двоичном формате. Однако шестнадцатеричные пары предоставляют нам для этого полезный обходной путь:
Литералы в двоичном формате и разделители цифр C++14
В C++14 мы можем назначать литералы в двоичном формате с помощью префикса 0b :
Поскольку длинные литералы трудночитаемы, в C++14 также добавлена возможность использования кавычек ( ' ) в качестве разделителя цифр.
Если ваш компилятор несовместим с C++14, он пожалуется, если вы попытаетесь использовать любой из этих приемов.
Печать десятичных, восьмеричных, шестнадцатеричных и двоичных чисел
По умолчанию C++ выводит значения в десятичном формате. Однако вы можете указать ему, что необходимо печатать в других форматах. Печать в десятичном, восьмеричном или шестнадцатеричном формате упрощается благодаря использованию std::dec , std::oct и std::hex :
Эта программа печатает:
Печать в двоичном формате немного сложнее, поскольку std::cout не имеет для этого встроенной возможности. К счастью, стандартная библиотека C++ включает в себя тип std::bitset (в заголовке <bitset> ), который сделает это за нас. Чтобы использовать std::bitset , мы можем определить переменную std::bitset и указать std::bitset , сколько бит мы хотим сохранить. Количество бит должно быть константой времени компиляции. std::bitset может быть инициализирован целочисленным значением без знака (в любом формате, включая десятичный, восьмеричный, шестнадцатеричный или двоичный).
Эта программа напечатает:
Мы также можем создать временный (анонимный) std::bitset для печати одного значения. В приведенном выше коде эта строка:
создает временный объект std::bitset с 4 битами, инициализирует его с помощью литерала 0b1010 , печатает его значение в двоичном формате, а затем отбрасывает временный std::bitset .
Магические числа, и почему это плохо
Рассмотрим следующий фрагмент:
Число, такое как 30 в приведенном выше фрагменте, называется магическим числом. Магическое число – это литерал (обычно число) в середине кода, не имеющий никакого контекста. Что значит 30? Хотя вы, наверное, догадываетесь, что в данном случае это максимальное количество студентов в классе, но это не совсем очевидно. В более сложных программах может быть очень сложно сделать вывод, что представляет собой жестко запрограммированное число, если нет комментария, объясняющего его.
Использование магических чисел обычно считается плохой практикой, потому что, помимо отсутствия контекста, для чего они используются, они создают проблемы, если значение необходимо изменить. Предположим, школа покупает новые парты, позволяющие увеличить количество учеников в классе с 30 до 35, и наша программа должна это отразить. Рассмотрим следующую программу:
Чтобы обновить нашу программу для использования нового размера класса, нам нужно будет обновить константу с 30 до 35. Но как насчет вызова setMax() ? Имеет ли это число 30 то же значение, что и другое число 30? Если да, то его следует обновить. В противном случае его следует оставить в покое, иначе мы можем сломать нашу программу где-нибудь еще. Если вы выполняете глобальный поиск и замену, вы можете случайно обновить аргумент setMax() , хотя он не должен был изменяться. Таким образом, вам нужно просмотреть весь код для каждого экземпляра литерала 30, а затем определить, нужно ли его изменить или нет. Это может занять много времени (и привести к ошибкам).
Хотя мы говорим «магические числа», это относится ко всем видам значений. Рассмотрим следующий пример:
В этом примере только одно число (100), но оно также используется в строках. Если мы решим обновить максимальное количество, скажем, на 200, нам придется обновить три разных случая, где встречается 100.
К счастью, существуют лучшие варианты (символьные константы). Об этом мы поговорим на следующем уроке.
Что такое литерал функции в Scala?
Что такое литерал функции в Scala и когда их использовать?
задан 09 марта ’11, 00:03
3 ответы
Функциональный литерал — это альтернативный синтаксис для определения функции. Это полезно, когда вы хотите передать функцию в качестве аргумента методу (особенно метод более высокого порядка, такой как свертка или операция фильтрации), но вы не хотите определять отдельную функцию. Функциональные литералы анонимны — у них нет имени по умолчанию, но вы можете дать им имя, привязав их к переменной. Функциональный литерал определяется так:
Вы можете привязать их к переменным:
Как я сказал ранее, функциональные литералы полезны для передачи в качестве аргументов функциям высшего порядка. Они также полезны для определения однострочных или вспомогательных функций, вложенных в другие функции.
Экскурсия по Скале дает неплохую ссылку на функциональные литералы (они называют их анонимными функциями).
ответ дан 09 мар ’11, в 04:03
Я думаю, было бы полезно указать на то, что литерал функции — это просто сахар по сравнению с FunctionN, что в целом может служить хорошим аргументом в пользу буквальной и «длинной» версии. Кроме того, хотя у меня есть +1, это обычно не «альтернативный синтаксис для определения функции [вы имели в виду метод . ]» как функции не методы — построены методы, которые только применяются к классам и сообщениям, которые могут быть отправлены им (классам). Функции являются «первоклассными ценностями» (объекты, поддерживающие apply и несколько других). — user166390
def add = (a: Int, b: Int) => a + b Разве это не то же самое? Я могу использовать это «добавить», чтобы перейти к другой функции — СЭС
вы также можете создать функциональный литерал из существующей функции с помощью оператора подчеркивания: def addTwoNumbers (a: Int, b: Int) = a + b; def add2 = addTwoNumbers _ — Adrians
Функции можно передавать как аргумент, а методы — нет. — Абдул Маннан
Может быть полезно сравнить функциональные литералы с другими видами литералов в Scala. литералы условный сахар для представляющие ценности некоторых типов язык считает особенно важными. Scala имеет целочисленные литералы, символьные литералы, строковые литералы и т. д. Scala рассматривает функции как значения первого класса, представленные в исходном коде с помощью функциональных литералов. Значения этих функций занимают особую тип функции, Например,
- 5 целочисленный литерал, представляющий значение в Int напишите
- ‘a’ символьный литерал, представляющий значение в Char напишите
- (x: Int) => x + 2 — это функциональный литерал, представляющий значение в Int => Int тип функции
Литералы часто используются как анонимные значения, то есть без предварительной привязки их к именованной переменной. Это помогает сделать программу более краткой и уместно, когда литерал не предназначен для повторного использования. Например:
Что такое литерал функции в Scala?
Что такое литерал функции в Scala и когда его следует использовать?
Литерал функции — это альтернативный синтаксис для определения функции. Он полезен в тех случаях, когда вы хотите передать функцию в качестве аргумента методу (особенно более высокого порядка, например, операции сложения или фильтрации), но не хотите определять отдельную функцию. Литералы функций анонимны — по умолчанию они не имеют имени, но вы можете дать им имя, привязав их к переменной. Литерал функции определяется следующим образом:
Их можно привязать к переменным:
Как я уже говорил, литералы функций полезны для передачи в качестве аргументов в функции более высокого порядка. Они также полезны для определения однострочных или вспомогательных функций, вложенных в другие функции.
В A Tour of Scala дается довольно хороший справочник по литералам функций (они называют их анонимными функциями).
Возможно, будет полезно сравнить литералы функций с другими видами литералов в Scala. Литералы — это нотационный сахар для представления значений некоторых типов, которые язык считает особенно важными. 2 Scala есть 2 целочисленные литералы, символьные литералы, строковые литералы и т.д. Scala рассматривает функции как значения первого класса, которые можно представить в исходном коде литералами функций. Эти значения функций находятся в специальном функциональном типе. Например,
- 5 — целочисленный литерал, представляющий значение типа Int .
- ‘a’ — символьный литерал, представляющий значение в типе Char .
- (x: Int) => x + 2 — функциональный литерал, представляющий значение в типе функции Int => Int .
Литералы часто используются как анонимные значения, то есть без привязки к именованной переменной. Это помогает сделать программу более лаконичной и подходит, когда литерал не предназначен для многократного использования. Например:
Пользовательские литералы в C++11
Более полугода прошло с момента принятия стандарта C++11. В сети можно найти много материалов посвященных новому стандарту, однако большинство из них касаются самых простых возможностей, самых сладких. Я говорю о лямбда-функциях, системе автоматического выведения типов, новых спецификаторах, умных указателях и т.д. Да, это действительно интересные вещи и, можно смело сказать, они одни из самых полезных и часто используемых. Но на них свет клином не сошелся, и новенький C++11 предлагает нам не только их.
Ниже я хочу рассказать о пользовательских литералах — весьма полезном средстве, хоть и не в повседневных целях.
Что такое литерал?
Литерал — это некоторое выражение, создающее объект. Литералы появились не только в C++11, они были и в C++03. Например, есть литералы для создания символа, строки, вещественных чисел, и т.д.
Все это литералы. С понятием литералов, думаю, мы разобрались. Самое время вернуться к C++11.
Пользовательские литералы в C++11
Как уже было отмечено выше, новый стандарт предлагает средства для создания пользовательских литералов. Существует две категории пользовательских литералов: сырые литералы (raw) и литералы для встроенных типов (cooked).
Стоит, однако, заметить, что C++ позволяет создавать только литералы-суфиксы. Иными словами, создать литералы префиксы (как, например, 0x), или префиксо-суфиксные (как «») — не получится.
Литералы для численных типов
Начнем с литералов для встроенных типов. Чтобы создать литерал для численных типов необходимо воспользоваться одной из двух сигнатур:
Использование литерала будет осуществляется следующим образом:
- литерал для целых чисел в качестве аргумента принимает unsigned long long
- литерал для вещественных чисел в качестве аргумента принимает long double
Ниже приведен пример литерала, преобразовывающего минуты в секунды.
Литералы для строковых типов
Для создания литерала этого типа, необходимо воспользоваться одной из следующих сигнатур:
Сигнатура выбирается в зависимости от типа строки:
Пример литерала преобразующего C-style строку в std::string приведен ниже.
Сырые литералы
Ну и наконец настало время сырого литерала. Сигнатура сырого литерала выглядит следующим образом:
Этот тип литералов приходит на помощь тогда, когда входное число надо разобрать посимвольно. Т.e. в этом случае число передается в оператор как строка. Если не совсем понятно, взгляните на приведенный ниже код:
Используя данный тип литералов, можно написать литерал преобразующий двоичное число в десятичное. Например вот так:
Существует еще одна сигнатура для сырых литералов. Основана она на применении Variadic Template:
Преимущества литералов на базе Variadic Template заключается в том, что они могут вычисляться на этапе компиляции. Тот же литерал преобразования двоичного числа в десятичное может быть переписан так:
У внимательно читателя мог возникнуть вопрос: «А что если создать и сырой литерал, и литерал для числа с одним и тем же именем? Какой литерал компилятор применит?». Стандарт по этому поводу дает точный ответ и говорит о попытке компилятора применить литералы в следующем порядке:
- operator «» _x (unsigned long long) или operator «» _x (long double)`
- operator «» _x (const char* raw)
- operator «» _x <‘c1’, ‘c2’, . ‘cn’>
Выводы
Бьёрн Страуструп на конференции Going Native 2012 приводил полезный пример использования литералов. Мне кажется, он наглядно демонстрирует факт повышения читаемости кода, а также снижает вероятность ошибиться.
Механизм пользовательских литералов — это полезный в некоторых случаях инструмент. Использовать его где попало не стоит. Подумайте дважды, прежде чем их использовать, ведь литералы коварны: они могут…