В языке C++ существуют два основных способа задания констант - это ключевые слова define (и директива препроцессора #define) и const (Начиная с версии C++ 11 также появилось выражение constexpr, с помощью которого можно задавать константные выражения, о ней будет кратко сказано в конце статьи). Константы - это фиксированные значения, которые не могут быть изменены. Их также принято называть литералами.
Можно задавать константы любых базовых типов данных, а сами константы подразделяются на следующие категории:
- строковые константы
- булевы константы
- целочисленные константы
- константы чисел с плавающей запятой (константы для десятичных дробей)
- символьные константы
Обращение к константам производится точно так же, как и к переменным, за исключением того факта, что их значение изменить никак нельзя, как только они были определены.
Давайте рассмотрим по порядку каждый из возможных видов констант и посмотрим как можно определять константы каждой категории с помощью директивы препроцессора #define и ключевого слова const.
Строковые константы
Строковые константы заключаются с обеих сторон в двойные кавычки ( " ). Строки содержат символы, схожие с символьными литералами и могут включать в себя как обычные символы, так и последовательности экранирования, а также универсальные символы.
Строковую константу можно разбить на несколько строк в редакторе кода через символ "обратного слеша" ( \ ), либо разделить части одной строковой константы с помощью пробелов.
Давайте посмотрим, как это делается в C++ при помощи ключевого слова define и директивы препроцессора #define:
#include <iostream>
using namespace std;
#define MY_STRING1 "This is my string"
#define MY_STRING2 "This " "is" " my" " string"
#define MY_STRING3 "This \
is my \
string"
int main() {
cout << "MYSTRING1 = " << MY_STRING1 << endl;
cout << "MYSTRING2 = " << MY_STRING2 << endl;
cout << "MYSTRING3 = " << MY_STRING3 << endl;
}
В этой простой программе мы задаём три строковых константы с именами MY_STRING1, MY_STRING2, MY_STRING3, содержимое которых будет одинаково. В этом можно легко убедиться, запустив программу с помощью Ctrl+F5 в среде разработки Microsoft Visual Studio:
MYSTRING1 = This is my string
MYSTRING2 = This is my string
MYSTRING3 = This is my string
Как мы видим, на экран трижды вывелось одно и то же значение, несмотря на то, что конструкции для определения констант немного отличались (указание целой строки, указание частей строки, разделенных пробелами и перенос частей строки по нескольким строкам исходного файла программы через символ "обратного слеша").
Теперь посмотрим, как того же эффекта определения констант можно добиться с помощью ключевого слова const:
#include <iostream>
using namespace std;
#define MY_STRING1 "This is my string"
#define MY_STRING2 "This " "is" " my" " string"
#define MY_STRING3 "This \
is my \
string"
const char* MY_ANOTHER_STRING1 = "My another string";
const char* MY_ANOTHER_STRING2 = "My " "another" " string";
const char* MY_ANOTHER_STRING3 = "My \
another \
string";
int main() {
cout << "MY_STRING1 = " << MY_STRING1 << endl;
cout << "MY_STRING2 = " << MY_STRING2 << endl;
cout << "MY_STRING3 = " << MY_STRING3 << endl;
cout << "MY_ANOTHER_STRING1 = " << MY_ANOTHER_STRING1 << endl;
cout << "MY_ANOTHER_STRING2 = " << MY_ANOTHER_STRING2 << endl;
cout << "MY_ANOTHER_STRING3 = " << MY_ANOTHER_STRING3 << endl;
}
Мы доработали наш предыдущий пример и добавили в него определение ещё трех констант MY_ANOTHER_STRING1, MY_ANOTHER_STRING2, MY_ANOTHER_STRING3, но уже с помощью ключевого слова const. Также обратите внимание на то, что нам пришлось после const дописать char*, что фактически означает указатель на константную строку. При запуске программы вы увидите следующий результат:
MY_STRING1 = This is my string
MY_STRING2 = This is my string
MY_STRING3 = This is my string
MY_ANOTHER_STRING1 = My another string
MY_ANOTHER_STRING2 = My another string
MY_ANOTHER_STRING3 = My another string
Булевы константы
Существуют два литерала булева типа, и оба они являются ключевыми словами в языке C++: это true и false. Для определения констант булева типа используем снова один из способов - через директиву препроцессора #define или ключевое слово const:
#include <iostream>
using namespace std;
#define MY_BOOL_CONST true
const bool MY_ANOTHER_BOOL_CONST = false;
int main() {
cout << "MY_BOOL_CONST = " << MY_BOOL_CONST << endl;
cout << "MY_ANOTHER_BOOL_CONST = " << MY_ANOTHER_BOOL_CONST << endl;
}
При запуске на экране консоли будет следующий результат:
MY_BOOL_CONST = 1
MY_ANOTHER_BOOL_CONST = 0
Целочисленные константы
Константы, представляющие собой целые числа, задаются по сути так же, как два предыдущих типа констант, - либо через директиву препроцессора #define или же через ключевое слово const. Есть некоторые особенности для определения констант целого типа, связанные с различными формами их представления/записи - например, константы можно задавать не только в привычной нам десятичной системе счисления, но и также в шестнадцатеричной, восьмеричной или двоичной системах счисления. В конце цифр, отвечающих за целочисленное значение константы, можно также добавлять специальные постфиксы вида u, l, ul, чтобы указать, что это константы типа unsigned int, long и unsigned long, соответственно. Кроме этого, если задаются константы в шестнадцатеричной системе счисления, Вы вправе указывать буквы от A до F как в верхнем, так и в нижнем регистре.
Давайте посмотрим все эти концепции на примере. Мы зададим различные комбинации целочисленных констант как через директиву #define, так и с помощью ключевого слова const и выведем все значения объявленных констант на экран консоли:
#include <iostream>
using namespace std;
#define MY_INT_CONSTANT 256 // константа типа int
#define MY_LONG_INT_CONSTANT 1000l // константа типа long
#define MY_UNSIGNED_INT_CONSTANT 50u // константа типа unsigned int
#define MY_UNSIGNED_LONG_INT_CONSTANT 77ul // константа типа unsigned long
#define MY_HEX_INT_CONSTANT 0xA1F // константа в шестнадцатеричной (hexademical) системе счисления, буквы A-F в верхнем регистре
#define MY_OCTAL_INT_CONSTANT 0774 // константа в восьмеричной (octal) системе счисления
#define MY_BINARY_INT_CONSTANT 0b11 // константа в двоичной (binary) системе счисления
#define MY_HEX_LOWERCASE_INT_CONSTANT 0x44b // константа в шестнадцатеричной (hexademical) системе счисления, буквы a-f в нижнем регистре
const int MY_INT_CONSTANT2 = 256;
const long MY_LONG_INT_CONSTANT2 = 1000l;
const unsigned int MY_UNSIGNED_INT_CONSTANT2 = 50u;
const unsigned long int MY_UNSIGNED_LONG_INT_CONSTANT2 = 77ul;
const int MY_HEX_INT_CONSTANT2 = 0xA1F;
const int MY_OCTAL_INT_CONSTANT2 = 0774;
const int MY_BINARY_INT_CONSTANT2 = 0b11;
const int MY_HEX_LOWERCASE_INT_CONSTANT2 = 0x44b;
int main() {
cout << "MY_INT_CONSTANT = " << MY_INT_CONSTANT << endl;
cout << "MY_INT_CONSTANT2 = " << MY_INT_CONSTANT2 << endl;
cout << "MY_LONG_INT_CONSTANT = " << MY_LONG_INT_CONSTANT << endl;
cout << "MY_LONG_INT_CONSTANT2 = " << MY_LONG_INT_CONSTANT2 << endl;
cout << "MY_UNSIGNED_INT_CONSTANT = " << MY_UNSIGNED_INT_CONSTANT << endl;
cout << "MY_UNSIGNED_INT_CONSTANT2 = " << MY_UNSIGNED_INT_CONSTANT2 << endl;
cout << "MY_UNSIGNED_LONG_INT_CONSTANT = " << MY_UNSIGNED_LONG_INT_CONSTANT << endl;
cout << "MY_UNSIGNED_LONG_INT_CONSTANT2 = " << MY_UNSIGNED_LONG_INT_CONSTANT2 << endl;
cout << "MY_HEX_INT_CONSTANT = " << MY_HEX_INT_CONSTANT << endl;
cout << "MY_HEX_INT_CONSTANT2 = " << MY_HEX_INT_CONSTANT2 << endl;
cout << "MY_OCTAL_INT_CONSTANT = " << MY_OCTAL_INT_CONSTANT << endl;
cout << "MY_OCTAL_INT_CONSTANT2 = " << MY_OCTAL_INT_CONSTANT2 << endl;
cout << "MY_BINARY_INT_CONSTANT = " << MY_BINARY_INT_CONSTANT << endl;
cout << "MY_BINARY_INT_CONSTANT2 = " << MY_BINARY_INT_CONSTANT2 << endl;
cout << "MY_HEX_LOWERCASE_INT_CONSTANT = " << MY_HEX_LOWERCASE_INT_CONSTANT << endl;
cout << "MY_HEX_LOWERCASE_INT_CONSTANT2 = " << MY_HEX_LOWERCASE_INT_CONSTANT2 << endl;
}
Как видно, мы выводим наши константы "парами" - для удобства, чтобы посмотреть что при выполнении программы значение будет идентично - неважно, задаём ли мы их через #define, либо через const. Вот что отобразится на консоли при запуске:
MY_INT_CONSTANT = 256
MY_INT_CONSTANT2 = 256
MY_LONG_INT_CONSTANT = 1000
MY_LONG_INT_CONSTANT2 = 1000
MY_UNSIGNED_INT_CONSTANT = 50
MY_UNSIGNED_INT_CONSTANT2 = 50
MY_UNSIGNED_LONG_INT_CONSTANT = 77
MY_UNSIGNED_LONG_INT_CONSTANT2 = 77
MY_HEX_INT_CONSTANT = 2591
MY_HEX_INT_CONSTANT2 = 2591
MY_OCTAL_INT_CONSTANT = 508
MY_OCTAL_INT_CONSTANT2 = 508
MY_BINARY_INT_CONSTANT = 3
MY_BINARY_INT_CONSTANT2 = 3
MY_HEX_LOWERCASE_INT_CONSTANT = 1099
MY_HEX_LOWERCASE_INT_CONSTANT2 = 1099
Константы чисел с плавающей запятой (константы для десятичных дробей)
Константы для чисел с плавающей запятой имеют в своём составе целочисленную часть, точку - для разделения целой части от десятичных знаков, дробную часть и экспоненциальную форму десятичной дроби, задаваемую с помощью латинской буквы E или малой e. Такие константы можно задавать либо в десятичном виде, либо в экспоненциальной форме.
- Если Вы определяете константу в десятичном виде, Вы должны включить точку, экспоненту или обе этих компоненты.
- Если Вы определяете константу в экспоненциальной форме, Вы должны включить целую часть, десятичную часть, или обе.
Для тех, кто забыл (или не знал), что такое экспоненциальная запись посмотрим на небольшом примере: число 10,5678 × 10-19 может быть записано в виде 10,5678E-19, т.е. после E идёт -19, что означает, что число умножается на 10 в -19-й степени. Точно так же происходит с положительными степенями: 5,6 × 1015 в экспоненциальной форме записывается как 5,6E15.
Теперь давайте посмотрим на примеры определения констант для чисел с плавающей точкой на языке C++:
#include <iostream>
using namespace std;
#define MY_SIMPLE_DOUBLE_CONST 5.6
#define MY_FLOAT_CONST_WITH_POSTFIX_AND_INT_PART 7.5f
#define MY_LONG_DOUBLE_CONST_EXPONENTIAL 10.765E-10L
#define MY_DOUBLE_CONST_EXPONENTIAL 10.765E-10
#define MY_DOUBLE_CONST_WITHOUT_INT_PART .543
#define MY_FLOAT_CONST_WITH_POSTFIX_AND_NO_INT_PART .123f
const double MY_SIMPLE_DOUBLE_CONST2 = 5.6;
const float MY_FLOAT_CONST_WITH_POSTFIX_AND_INT_PART2 = 7.5f;
const long double MY_LONG_DOUBLE_CONST_EXPONENTIAL2 = 10.765E-10L;
const double MY_DOUBLE_CONST_EXPONENTIAL2 = 10.765E-10;
const double MY_DOUBLE_CONST_WITHOUT_INT_PART2 = .543;
const float MY_FLOAT_CONST_WITH_POSTFIX_AND_NO_INT_PART2 = .123f;
int main() {
cout << "MY_SIMPLE_DOUBLE_CONST = " << MY_SIMPLE_DOUBLE_CONST << endl;
cout << "MY_SIMPLE_DOUBLE_CONST2 = " << MY_SIMPLE_DOUBLE_CONST2 << endl;
cout << "MY_FLOAT_CONST_WITH_POSTFIX_AND_INT_PART = " << MY_FLOAT_CONST_WITH_POSTFIX_AND_INT_PART << endl;
cout << "MY_FLOAT_CONST_WITH_POSTFIX_AND_INT_PART2 = " << MY_FLOAT_CONST_WITH_POSTFIX_AND_INT_PART2 << endl;
cout << "MY_LONG_DOUBLE_CONST_EXPONENTIAL = " << MY_LONG_DOUBLE_CONST_EXPONENTIAL << endl;
cout << "MY_LONG_DOUBLE_CONST_EXPONENTIAL2 = " << MY_LONG_DOUBLE_CONST_EXPONENTIAL2 << endl;
cout << "MY_DOUBLE_CONST_EXPONENTIAL = " << MY_DOUBLE_CONST_EXPONENTIAL << endl;
cout << "MY_DOUBLE_CONST_EXPONENTIAL2 = " << MY_DOUBLE_CONST_EXPONENTIAL2 << endl;
cout << "MY_DOUBLE_CONST_WITHOUT_INT_PART = " << MY_DOUBLE_CONST_WITHOUT_INT_PART << endl;
cout << "MY_DOUBLE_CONST_WITHOUT_INT_PART2 = " << MY_DOUBLE_CONST_WITHOUT_INT_PART2 << endl;
cout << "MY_FLOAT_CONST_WITH_POSTFIX_AND_NO_INT_PART = " << MY_FLOAT_CONST_WITH_POSTFIX_AND_NO_INT_PART << endl;
cout << "MY_FLOAT_CONST_WITH_POSTFIX_AND_NO_INT_PART2 = " << MY_FLOAT_CONST_WITH_POSTFIX_AND_NO_INT_PART2 << endl;
}
Аналогично тому, как мы поступили с демонстрацией целочисленных констант, здесь мы также выводим константы "парами", чтобы сравнить значения тех, что заданы через директиву #define с теми, что заданы через const. Результат этой программы на экране консоли:
MY_SIMPLE_DOUBLE_CONST = 5.6
MY_SIMPLE_DOUBLE_CONST2 = 5.6
MY_FLOAT_CONST_WITH_POSTFIX_AND_INT_PART = 7.5
MY_FLOAT_CONST_WITH_POSTFIX_AND_INT_PART2 = 7.5
MY_LONG_DOUBLE_CONST_EXPONENTIAL = 1.0765e-09
MY_LONG_DOUBLE_CONST_EXPONENTIAL2 = 1.0765e-09
MY_DOUBLE_CONST_EXPONENTIAL = 1.0765e-09
MY_DOUBLE_CONST_EXPONENTIAL2 = 1.0765e-09
MY_DOUBLE_CONST_WITHOUT_INT_PART = 0.543
MY_DOUBLE_CONST_WITHOUT_INT_PART2 = 0.543
MY_FLOAT_CONST_WITH_POSTFIX_AND_NO_INT_PART = 0.123
MY_FLOAT_CONST_WITH_POSTFIX_AND_NO_INT_PART2 = 0.123
Обратите внимание, что литералы, записанные в экспоненциальной форме, автоматически конвертировались компилятором в несколько иную форму: вместо 10.765E-10L на экране консоли вывелось 1.0765e-09, но фактически это одно и то же число: 10,765 × 10-10 и 1,0765 × 10-9 суть одно и то же.
Символьные константы
Символьные константы в языке C++ заключаются с обеих сторон в одинарные кавычки ( ' ). Если литерал начинается с буквы L (в данном случае - исключительно с заглавной буквы!), то это означает, что это литерал расширенного символа и должен храниться в переменной с типом wchar_t. В противном же случае - при отсутствии заглавной L перед символьной константой - литерал представляет собой обычный символ и может храниться в переменной с типом char.
Символьный литерал может быть одним из следующего:
- обычным символом. Примеры: 'a', 'F', '&'
- последовательностью экранирования (escape sequence). Примеры: '\n', '\t', '\r'
- универсальным символом. Пример: '\u02C0'
В C++ есть определенные символы, которые имеют специальное значение в том случае, когда они предваряются символом "обратного слеша" ( \ ). К ним относятся:
- спецсимвол перевода на новую строку: '\n'
- спецсимвол возврата каретки: '\r'
- спецсимвол табуляции: '\t'
- символ \ : '\\'
- символ ' : '\''
- символ ? : '\?'
- символ сигнала или звонка: '\a'
- символ клавиши Backspace: '\b'
- символ вертикальной табуляции: '\v'
- восьмеричное число от 1 до 3 цифр: '\ooo'
- шестнадцатеричное число от 1 и более цифр: '\xhh...'
Давайте посмотрим на примеры использования символьных констант в программе на C++:
#include <iostream>
using namespace std;
#define MY_CHAR_Z 'Z'
#define MY_WCHAR_T L'A'
#define MY_OCTAL_CHAR '\07'
#define MY_HEX_CHAR '\x0F1'
const char MY_CHAR_Z2 = 'Z';
const wchar_t MY_WCHAR_T2 = L'A';
const char MY_OCTAL_CHAR2 = '\07';
const char MY_HEX_CHAR2 = '\x0F1';
int main() {
cout << "MY_CHAR_Z = " << MY_CHAR_Z << endl;
cout << "MY_CHAR_Z2 = " << MY_CHAR_Z2 << endl;
cout << "MY_WCHAR_T = " << MY_WCHAR_T << endl;
cout << "MY_WCHAR_T2 = " << MY_WCHAR_T2 << endl;
cout << "MY_OCTAL_CHAR = " << MY_OCTAL_CHAR << endl;
cout << "MY_OCTAL_CHAR2 = " << MY_OCTAL_CHAR2 << endl;
cout << "MY_HEX_CHAR = " << MY_HEX_CHAR << endl;
cout << "MY_HEX_CHAR2 = " << MY_HEX_CHAR2 << endl;
}
При запуске программы на экране консоли появится:
MY_CHAR_Z = Z
MY_CHAR_Z2 = Z
MY_WCHAR_T = 65
MY_WCHAR_T2 = 65
MY_OCTAL_CHAR =
MY_OCTAL_CHAR2 =
MY_HEX_CHAR = ё
MY_HEX_CHAR2 = ё
Обратите внимание, что для константы MY_OCTAL_CHAR и MY_OCTAL_CHAR2 ничего не вывелось. Зато Вы должны были услышать характерный звук сигнала (alert или bell) при запуске программы. Это оттого, что значение этих констант задано как '\07' в восьмеричной системе счисления, т.е. фактически 7 в десятичной. А спецсимвол экранирования '\a' (от англ. alert), который отвечает за звук сигнала, также имеет этот код 7. Этим и объясняется то, что на экран ничего не выводится, зато в динамиках Вы услышите характерный сигнал.
Выражение constexpr
Выражение constexpr позволяет задать константное выражение, например:
constexpr int x = 100;
constexpr float y = 77.5;
Оно появилось, начиная с версии стандарта языка C++11. Основное различие между переменными const и constexpr заключается в том, что инициализация переменной, объявленной при помощи const может быть отложено до времени выполнения. Переменная constexpr должна быть инициализирована во время компиляции.
Заключение
Попробуйте задать константы различных типов в Вашей программе на C++ и потренироваться с их использованием. Уверен, у Вас получится! А результатами можете поделиться в комментариях к статье. Также не стесняйтесь задать свои вопросы, если они появились.