Константы в C++. Ключевые слова define и const

User Rating: 4 / 5

В языке C++ существуют два основных способа задания констант - это ключевые слова define (и директива препроцессора #define) и const (Начиная с версии C++ 11 также появилось выражение constexpr, с помощью которого можно задавать константные выражения, о ней будет кратко сказано в конце статьи). Константы - это фиксированные значения, которые не могут быть изменены. Их также принято называть литералами.

Можно задавать константы любых базовых типов данных, а сами константы подразделяются на следующие категории:

  1. строковые константы
  2. булевы константы
  3. целочисленные константы
  4. константы чисел с плавающей запятой (константы для десятичных дробей)
  5. символьные константы

Обращение к константам производится точно так же, как и к переменным, за исключением того факта, что их значение изменить никак нельзя, как только они были определены.

Info icon by Icons8НА ЗАМЕТКУ
Строго говоря, ключевое слово и конструкция #define является директивой препроцессора, и она создаёт макрос. Она выполняется до этапа компиляции, выполняя текстовую замену в исходном коде всех вхождений идентификатора соответствующим значением. Это не равноценно заданию констант через ключевое слово const.

Давайте рассмотрим по порядку каждый из возможных видов констант и посмотрим как можно определять константы каждой категории с помощью директивы препроцессора #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_STRING1MY_ANOTHER_STRING2MY_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++ заключаются с обеих сторон в одинарные кавычки ( ' ). Если литерал начинается с буквы (в данном случае - исключительно с заглавной буквы!), то это означает, что это литерал расширенного символа и должен храниться в переменной с типом 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++ и потренироваться с их использованием. Уверен, у Вас получится! А результатами можете поделиться в комментариях к статье. Также не стесняйтесь задать свои вопросы, если они появились.

 

Яндекс.Метрика