Константы и литералы

Константы делают код читаемым и надёжным, а параметр PARAMETER гарантирует, что важная величина не изменится по ошибке.

Именованная константа (parameter) — это идентификатор, которому при объявлении присвоено фиксированное значение, не подлежащее изменению во время выполнения; задаётся атрибутом parameter.

В численном коде постоянно встречаются неизменные величины: число π, ускорение свободного падения, размерности массивов, физические коэффициенты. Зашивать их прямо в формулы числами («магические константы») — рецепт ошибок и нечитаемого кода. Fortran предлагает аккуратный механизм именованных констант через атрибут parameter, а заодно — богатый синтаксис числовых, логических и символьных литералов. Этот урок учит грамотно вводить постоянные значения и правильно записывать литералы всех типов.

Почему «магические числа» опасны

Прежде чем разбирать синтаксис, стоит понять, какую болезнь лечат именованные константы. «Магическим числом» называют числовой литерал, встречающийся прямо в коде без пояснения смысла: if (x > 273.15), volume = 4.18879 * r**3. Проблем здесь сразу несколько. Во-первых, читатель не понимает, что за число: 273.15 — это ноль Цельсия в кельвинах, но это знание не записано нигде, и через год вы сами забудете. Во-вторых, одно и то же значение часто повторяется в десятках мест, и если его понадобится уточнить (скажем, взять больше знаков), придётся найти и поправить каждое вхождение, рискуя пропустить одно. В-третьих, велик риск опечатки: набирая длинную константу вручную в пятый раз, легко переставить цифры, и такой баг почти невозможно заметить глазами.

Именованная константа снимает все три проблемы разом. Имя abs_zero_celsius объясняет смысл, значение задано единожды, а опечатка в имени будет поймана компилятором (в отличие от опечатки в числе). Это одна из тех дисциплин, что отличают профессиональный код от студенческого: в серьёзных численных библиотеках вы почти не встретите «голых» чисел в формулах — все значимые величины вынесены в константы с говорящими именами. Привычку выносить любое неочевидное число в parameter стоит выработать с самого начала.

Именованные константы: parameter

Константа объявляется как обычная переменная, но с атрибутом parameter и обязательной инициализацией. После этого попытка присвоить ей новое значение — ошибка компиляции, что и нужно: важная величина защищена.

program constants
  implicit none
  integer, parameter :: dp = selected_real_kind(15)
  real(dp), parameter :: pi = 3.141592653589793_dp
  real(dp), parameter :: g = 9.80665_dp        ! ускорение свободного падения
  integer, parameter :: max_iter = 1000        ! предел итераций
  real(dp) :: radius, area

  radius = 2.5_dp
  area = pi * radius**2
  print *, "Площадь круга:", area
  print *, "Свободное падение за 1 с:", 0.5_dp * g
end program constants

Вывод:

Площадь круга:   19.634954084936208
Свободное падение за 1 с:   4.9033250000000003

Польза тройная. Во-первых, читаемость: pi * radius**2 понятнее, чем 3.14159 * radius**2. Во-вторых, единая точка правки: захотели больше знаков π — меняете в одном месте. В-третьих, защита от ошибки: случайное pi = 3.0 где-то в коде не скомпилируется. Размер массива через parameter — особенно частый и важный приём: одна константа управляет размерностью по всей программе.

Константа как размер массива — ключевой идиом

Один сценарий заслуживает отдельного внимания, потому что в Fortran он встречается на каждом шагу: задание размерности массивов через именованную константу. Объявив integer, parameter :: n = 1000, вы дальше пишете real(dp) :: a(n), b(n), matrix(n, n), и все массивы программы связаны с одним числом. Захотели прогнать расчёт на сетке 2000 вместо 1000 — меняете единственную строку, и вся программа согласованно перестраивается; ни один цикл do i = 1, n править не нужно. Без этого приёма пришлось бы вылавливать число 1000 по всему коду, и стоило бы пропустить одно вхождение — получили бы выход за границу массива, классический и трудноуловимый дефект. Поскольку parameter известен на этапе компиляции, такие массивы могут размещаться статически, что важно для производительности численного кода. Именно поэтому опытные Fortran-программисты почти никогда не пишут размер массива числом напрямую — он почти всегда приходит из именованной константы.

Чем parameter отличается от констант в C и Python

Сравнение с соседними языками проясняет природу parameter. В C ту же роль играют #define MAX 1000 (текстовая подстановка препроцессором, без типа и проверок) и const int max = 1000; (типизированная константа). Fortran-овский parameter ближе ко второму, но строже: его значение обязано быть вычислимо на этапе компиляции, поэтому компилятор гарантированно подставит его как литерал и проверит тип. В Python настоящих констант нет вовсе — есть лишь соглашение писать «неизменяемые» имена заглавными буквами (PI = 3.14159), но язык не мешает их переписать; защита держится на дисциплине, а не на компиляторе. Fortran же делает неизменность частью контракта: попытка присвоить parameter новое значение — ошибка сборки, а не вопрос воспитания. Эта жёсткость — осознанный выбор языка, ориентированного на надёжность долгоживущих расчётных программ.

Целочисленные литералы

Целые литералы записываются естественно. Кроме десятичных, Fortran понимает двоичные, восьмеричные и шестнадцатеричные константы — они полезны при работе с битовыми масками.

program int_literals
  implicit none
  integer :: dec, bin, oct, hex
  dec = 255
  bin = b'11111111'   ! двоичное -> 255
  oct = o'377'        ! восьмеричное -> 255
  hex = z'FF'         ! шестнадцатеричное -> 255
  print *, dec, bin, oct, hex
end program int_literals

Вывод:

         255         255         255         255

Префиксы b'...', o'...', z'...' задают систему счисления. К целому литералу также можно добавить KIND-суффикс через подчёркивание: 100_i8.

Зачем вообще нужны другие системы счисления, если число всё равно одно? Дело в выразительности для определённых задач. Шестнадцатеричная запись компактна и наглядна при работе с битовыми масками, флагами оборудования и адресами: z'FF' сразу читается как «все восемь младших бит установлены», тогда как десятичное 255 этого не показывает. Двоичная форма прямо рисует биты, что удобно при описании регистров устройств. В чисто вычислительном Fortran-коде эти формы встречаются нечасто, но в системном программировании, при разборе бинарных форматов файлов и взаимодействии с аппаратурой они незаменимы. Полезно помнить, что система счисления — это лишь форма записи в исходнике: в памяти 255, z'FF' и b'11111111' — одно и то же целое число, и компилятор не делает между ними никакой разницы после разбора.

Вещественные и комплексные литералы

Вещественный литерал обязан содержать десятичную точку или экспоненту, иначе Fortran сочтёт его целым. Экспоненциальная запись использует e для одинарной и (исторически) d для двойной точности, но современный путь — KIND-суффикс.

program real_literals
  implicit none
  integer, parameter :: dp = selected_real_kind(15)
  real(dp) :: a, b, c
  complex(dp) :: z

  a = 3.0_dp          ! точка обязательна
  b = 6.022e23_dp     ! число Авогадро
  c = 1.6e-19_dp      ! заряд электрона
  z = (1.0_dp, -2.0_dp)   ! комплексное 1 - 2i
  print *, a, b
  print *, "Re(z) =", real(z), " Im(z) =", aimag(z)
end program real_literals

Вывод:

   3.0000000000000000   6.0220000000000000E+023
 Re(z) =   1.0000000000000000  Im(z) =  -2.0000000000000000

Комплексный литерал — это пара (действительная, мнимая) в круглых скобках. Функции real и aimag извлекают части. Обратите внимание: 3 — это целое, а 3.0 — вещественное; разница принципиальна для деления, о чём в следующем уроке.

Логические и символьные литералы

Логических значений ровно два, и пишутся они с точками: .true. и .false.. Точки — часть синтаксиса, забывать их нельзя. Символьные литералы заключают в одинарные или двойные кавычки; кавычку внутри строки удваивают.

program log_char
  implicit none
  logical :: flag
  character(len=30) :: msg, quote
  flag = .true.
  msg = "Расчёт завершён"
  quote = "Он сказал ""готово"" вслух"   ! удвоенные кавычки внутри
  print *, flag
  print *, trim(msg)
  print *, trim(quote)
end program log_char

Вывод:

 T
 Расчёт завершён
 Он сказал "готово" вслух

Двойная кавычка "" внутри строки в двойных кавычках превращается в одну литеральную кавычку — это способ вставить кавычку в текст. Тот же приём с одинарными.

Способ экранирования через удвоение кавычки заметно отличается от того, к чему привыкли в C, Java или Python, где для той же цели используют обратную косую черту: "\"". В Fortran обратная косая черта не имеет особого смысла внутри строки (по умолчанию это обычный символ), а кавычку вставляют, написав её дважды. У этого подхода есть приятное следствие: путь к файлу в Windows вроде "C:\data\input.txt" в Fortran пишется буквально, без удвоения слешей, тогда как в C его пришлось бы записывать как "C:\\data\\input.txt". Это историческое решение языка, и его стоит держать в голове, переходя с C-подобных языков: здесь экранируют не слешем, а повтором символа.

Как работает под капотом

Чем parameter отличается от обычной переменной на уровне машины? Значение именованной константы вычисляется на этапе компиляции и часто вообще не занимает отдельной ячейки памяти: компилятор подставляет его прямо в код там, где константа используется (это называется constant folding). Поэтому parameter не только защищает значение, но и не стоит ничего во время выполнения — это «бесплатная» абстракция. Кроме того, выражение для константы должно быть вычислимо на этапе компиляции: real(dp), parameter :: two_pi = 2.0_dp * pi допустимо, потому что pi тоже константа, а вот инициализировать parameter результатом ввода с клавиатуры нельзя. Литералы же компилятор разбирает на стадии лексического анализа: префикс z'FF' или суффикс _dp сообщают ему, как именно интерпретировать последовательность символов и в каком типе хранить результат.

Частые ошибки

  • Вещественный литерал без точки. x = 3 при real :: x работает, но 1/3 (оба целые) даст 0; пишите 3.0, 1.0/3.0.
  • Забытые точки у логических. flag = true — ошибка; нужно .true. и .false..
  • Попытка изменить parameter. Присваивание именованной константе не компилируется — это и есть её защитная функция.
  • Магические числа. Числа прямо в формулах усложняют сопровождение; выносите их в именованные константы.
  • Кавычка внутри строки. Чтобы вставить " в строку с двойными кавычками, её удваивают: "".

Итоги

  • Атрибут parameter создаёт именованную константу с фиксированным значением; изменить её нельзя.
  • Константы повышают читаемость, дают единую точку правки и защищают важные величины.
  • Размер массива удобно задавать через integer, parameter — одна правка меняет всю программу.
  • Целые литералы поддерживают двоичную, восьмеричную и шестнадцатеричную запись (b'..', o'..', z'..').
  • Вещественный литерал требует точки или экспоненты; комплексный — пара в скобках (re, im).
  • Логические литералы пишут с точками .true./.false.; кавычку в строке удваивают.
Проверьте себя
1. Что гарантирует атрибут parameter у объявления?
AПеременную можно менять только в циклах
BЗначение фиксировано и попытка его изменить — ошибка компиляции
CПеременная становится глобальной
DЗначение задаётся пользователем при запуске
2. Как записать комплексный литерал 1 − 2i?
A1 - 2i
Bcomplex(1, -2)
C(1.0_dp, -2.0_dp)
D1.0 - 2.0*i
3. Как правильно записать логическое значение «истина»?
Atrue
BTrue
C.true.
D1