<?xml version="1.0" encoding="windows-1251"?>
<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:xlink="http://www.w3.org/1999/xlink">
<description>
  <title-info>
  <genre>sci_tech</genre>
  <genre>comp_osnet</genre>
  <genre>comp_programming</genre>
  <author>
   <first-name>Сергей</first-name>
   <last-name>Посохов</last-name>
   <home-page>http://www.r-5.org</home-page>
  </author>
  <book-title>Protected C Coding Style for Operating System/2 (05.05.2021)</book-title>
  <coverpage>
   <image xlink:href="#BookCover"/>
  </coverpage>
  <annotation>
   <image xlink:href="#BookCover"/>
   <empty-line/>
   <p>Книга содержит примеры на языке C, позволяющие создавать защищённое программное обеспечение в операционной системе OS/2.</p>
   <empty-line/>
  </annotation>
  <keywords>C, OS/2, Coding Style</keywords>
  <date value="2021-05-05">05.05.2021</date>
  <lang>ru</lang>
  <src-lang>ru</src-lang>
  </title-info>
  <document-info>
  <author>
   <first-name>Сергей</first-name>
   <last-name>Посохов</last-name>
   <home-page>http://www.r-5.org</home-page>
  </author>
  <date value="2021-05-05">05.05.2021</date>
  <src-url>http://www.r-5.org</src-url>
  <id>protected-c</id>
  <version>20210505</version>
  </document-info>
</description>
<body>
<!-- -#- -->

<section><!-- 1 -->
<title><p>Первая страница</p></title>
<p>
В этой книге кратко рассказано о диалекте языка C, который
позволяет создавать программное обеспечение с высокими
требованиями к качеству и скорости работы. В программах на
этом языке действуют строгие правила для работы с данными
(особенно с указателями), а также правила приведения типов
(которые связаны между собой в виде дерева). Есть также
правила, задающие внешний вид условий и циклов, порядок
действий внутри функций, и даже отступы и пробелы.
</p>
<empty-line/>
<p>
В качестве примера удобно было бы рассмотреть программу,
которая печатает приглашение или вызывает внешнюю функцию,
после чего печатает полученные от неё данные и завершается:
</p>
<empty-line/>
<p>
<code>
   &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
   &#9474; Main.= &#9474;
   &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
   
   #include &lt;API.h&gt;
   
   #include "PrimeNumbers.h"
   #include "PrimeNumbers.="
   
   #import stdlib, stdio
   #import prime-numbers
   
   VOID &gt;&gt; (PCHAR Arg[][], INT Length)
   {
     if (Length != 2)
     {
       Say ("Usage: %s%s", Arg[0][0], " N");
     }
     else
     {
       Say ("== Prime Numbers ==");
   
       PrimeNumbers (IntegerFrom (&#038;_, Arg[1][0]), (INT N) {
         Say ("%d", N);
       });
     }
   }</code>
</p>
<empty-line/>
<p>
Здесь функция "Say()" печатает на экран строки и числа, добавляя
к ним переходы на новую строку, функция "PrimeNumbers()" находит
простые числа, а функция "IntegerFrom()" переводит строку в число.
Символ подчёркивания означает, что эта функция заполняет внешнюю
переменную по указателю (поскольку это не вычислительная функция),
и она может быть использована как её возвращаемое значение.
</p>
<empty-line/>
<p>
Со времён языка C остались заголовочные файлы (они позволяют
не уходить от обыкновенного C и составлять программы на этом
его диалекте в точности так же, как это делали раньше). Здесь
файл с заголовками функций и описанием типов выглядит так:
</p>
<empty-line/>
<p>
<code>
   &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
   &#9474; PrimeNumbers.h &#9474;
   &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
   
   # prime-numbers
   
   VOID PrimeNumbers (INT, ((INT) VOID) FP);</code>
</p>
<empty-line/>
<p>
Как видно, здесь дано описание функции "PrimeNumbers()", и она
получает указатель на функцию, которую вызывает, чтобы передать
найденное простое число на уровень выше (флажок "FP" означает,
что это может быть безымянная функция, которая будет способна
увидеть переменные, объявленные там, где она была написана).
Здесь также сказано, что эта функция относится к пространству
имён "prime-numbers".
</p>
<empty-line/>
<p>
Поиск простых чисел выглядит так:
</p>
<empty-line/>
<p>
<code>
   &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
   &#9474; PrimeNumbers.= &#9474;
   &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
   
   # prime-numbers
   
   VOID PrimeNumbers (INT N, ((INT) VOID) FP ReportPrime)
   {
     BYTE IsPrime (INT N) -&gt; 1 | 0
     {
       BYTE WR Is_prime = 1;
   
       for (INT Number = 2; Number &lt; N; Number ++)
       {
         if (N % Number == 0)
         {
           Is_prime = 0; break;
         }
       }
   
       return Is_prime;
     }
   
     for (INT Number = 0; Number &lt; N; Number ++)
     {
       if (Number-&gt;IsPrime ())
       {
         ReportPrime (Number);
       }
     }
   }
   
   #export PrimeNumbers</code>
</p>
<empty-line/>
<p>
Здесь внутри функции "PrimeNumbers()" описана другая функция
(эта возможность есть в недавних расширениях языка C, хотя её
нет в стандарте), и она перебирает все числа от 2 до заданного
числа и вычисляет остаток от его деления на каждое из них. Все
чётные числа сразу же отбрасываются, а нечётные - проверяются в
цикле (и на каждом шаге число проверок возрастает).
</p>
<empty-line/>
<p>
Флажок "-&gt;[]" в заголовке функции "IsPrime()" означает, что она
может быть поставлена в оператор условия:
</p>
<empty-line/>
<p>
<code>
   VOID PrimeNumbers (INT N, ((INT) VOID) FP ReportPrime)
   {
     BYTE IsPrime (INT N) -&gt; 1 | 0
     {
       ...
     }
   
     ...
   
   }</code>
</p>
<empty-line/>
<p>
И оператор условия в примере выше выглядит таким образом:
</p>
<empty-line/>
<p>
<code>
   VOID PrimeNumbers (INT N, ((INT) VOID) FP ReportPrime)
   {
       ...
   
       if (Number-&gt;IsPrime ())
       {
         ...
       }
     }
   }</code>
</p>
<empty-line/>
<p>
А если бы требовалось поддерживать совместимость со стандартным
C, его можно было бы записать вот так:
</p>
<empty-line/>
<p>
<code>
   VOID PrimeNumbers (INT N, ((INT) VOID) FP ReportPrime)
   {
       ...
   
       if (IsPrime (Number))
       {
         ...
       }
     }
   }</code>
</p>
<empty-line/>
<p>
И очень часто именно это и требуется: простой и удобный язык
программирования, способный работать на уровне битов и байтов,
и в то же время позволяющий составлять программы как на "чистом
C", так и на более современном языке, подобном C.
</p>
<empty-line/>
<p>
Объектно-ориентированное программирование здесь не подходит:
"объекты" полностью засоряют кеш процессора, что заметно снижает
скорость. Это простой алгоритмический язык, и в нём очень многое
отличается от ООП.
</p>
</section><!-- 1 -->
<section><!-- 1 -->
<title><p>&nbsp;</p></title>
<p>&nbsp;</p>
</section><!-- 1 -->
<section><!-- 1 -->
<title><p>Описание и вызов функций</p></title>
<p>
Функции в современных языках программирования - наименьшие
возможные составляющие части программ (при том, что ветки
операторов условия и циклы - тоже функции). Они могут быть
вложенными и могут быть собраны в пространства имён, которые
тоже могут быть вложенными - и всё это позволяет разделить
программу на составляющие части.
</p>
<section><!-- 2 -->
<title><p>Точка начала выполнения</p></title>
<p>
Точка начала выполнения выглядит как функция с названием
"&gt;&gt;", не имеющая возвращаемого значения:
</p>
<empty-line/>
<p>
<code>
   VOID &gt;&gt; ()
   {
     stdio:Print ("Hello, World!");
   }</code>
</p>
<empty-line/>
<p>
А можно написать её и так:
</p>
<empty-line/>
<p>
<code>
   #import stdio
   
   VOID &gt;&gt; ()
   {
     Print ("Hello, World!");
   }</code>
</p>
<empty-line/>
<p>
В заголовке этой функции могут быть указаны два параметра,
позволяющие добраться до командной строки:
</p>
<empty-line/>
<p>
<code>
   #import stdio
   
   VOID &gt;&gt; (PCHAR Arg[][], INT Length)                                            /* "Program.exe;string;string..." */
   {
     VOID SayWords () // Arg, Length
     {
       for (INT Counter = 0; Counter &lt; Length; Counter ++)
       {
         Print (Arg[Counter][0]);
   
         if (Counter != Length - 1) Print (" ");
         if (Counter == Length - 1) Print ("\n");
       }
     }
   
     if (Length &gt; 1)
     {
       SayWords (); Exit ();
     }
     else
     {
       Say ("Usage: %s {string}", Arg[0][0]); Exit (1);
     }
   }</code>
</p>
<empty-line/>
<p>
Как видно, командная строка передаётся в массиве указателей
"Arg", и снаружи он задаётся таким образом:
</p>
<empty-line/>
<p>
<code>
   PCHAR Arg[][] = {
                     { "Program.exe", "" },
                     { "Hello,",      "" },
                     { "World!",      "" }
                   }, ULONG Length = array_length (Arg);
   
   &gt;&gt; (Arg, Length);</code>
</p>
<empty-line/>
<p>
А можно и так:
</p>
<empty-line/>
<p>
<code>
   PCHAR Arg[][] = {
                     { "Program.exe", "" },
                     { "/I",          "" },
                     { "/Verbose",    "" },
                     ...
                   };</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   PCHAR Arg[][] = {
                     { "Program.exe", "" },
                     { "-i",          "" },
                     { "--verbose",   "" },
                     ...
                   };</code>
</p>
<empty-line/>
<p>
И двумерный массив указателей позволяет получить доступ ко
всем строкам, вне зависимости от того, как они были заданы.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Порядок действий внутри функций</p></title>
<p>
Для того, чтобы программы на этом диалекте языка C было легко
читать, все функции, циклы и ветки операторов условий должны
быть приведены к единому виду:
</p>
<empty-line/>
<p>
<code>
   VOID &gt;&gt; ()
   {
    &#9484;&#9472;
    &#9474; f (x)
    &#9474; =, +=, ++ ...
    &#9492;&#9472;
    &#9484;&#9472;
    &#9474; for (...)
    &#9474; =, +=, ++ ...
    &#9492;&#9472;
    &#9484;&#9472;
    &#9474; if (...)
    &#9492;&#9472;
    &#9484;&#9472;
    &#9474; return или if/return
    &#9492;&#9472;
   }</code>
</p>
<empty-line/>
<p>
То есть, каждая функция должна содержать объявления переменных,
после чего она может заполнить внешние переменных нулями, затем
выполнить расчёты и вызовы других функций, и затем внутри неё
могут появиться операторы циклов и операторы условий (при этом
условия должны быть сдвинуты в конец функции), а завершать её
должен единственный оператор возврата - вот так:
</p>
<empty-line/>
<p>
<code>
   ULONG RC Factorial (ULONG N)
   {
     if (N == 0 || N == 1)
     {
       return 1;
     }
     else
     {
       return N * Factorial (N - 1);
     }
   }</code>
</p>
<empty-line/>
<p>
Но не так:
</p>
<empty-line/>
<p>
<code>
   ULONG RC Factorial (ULONG N)
   {
     if (N == 0 || N == 1)
     {
       return 1;                                                                  /* !!! */
     }
   
     return N * Factorial (N - 1);
   }</code>
</p>
<empty-line/>
<p>
И если, например, поставить оператор цикла после оператора
условия - компилятор сообщит об ошибке:
</p>
<empty-line/>
<p>
<code>
   ULONG PerformAction (PSTRUCT WR Out, ULONG Length, PSTRUCT Request)
   {
     ULONG Rc = SysPerformAction (Out, Length, Request);
   
     if (Rc == NO_ERROR)
     {
       PrintString ("All done.\n");
     }
     else
     {
       HandleError (Result);
     }
   
     for (INT Counter = 0; Counter &lt; Length; Counter ++)                          /* !!! */
     {
       ...
     }
   
     return Rc;
   }</code>
</p>
<empty-line/>
<p>
Кроме того, внутри каждой функции действуют правила о том,
какие переменные могут быть изменяемыми: "все изменяемые
переменные должны быть объявлены до первого вызова функции
внутри этой функции, а удаление переменной или области памяти
должно выполняться внутри той же функции, где она была создана".
Компилятор следит за выполнением этих правил, обращая внимание
на флажки в заголовках функций - такие, как "WR".
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Принимаемые и возвращаемые значения</p></title>
<p>
Параметры, переданные на вход функции, доступны внутри неё
с правами только на чтение. То есть, копирование любого из
них во временную переменную и её изменение возможны:
</p>
<empty-line/>
<p>
<code>
   VOID &gt;&gt; (PCHAR Arg[][], INT Length)
   {
     INT WR X = Length; X ++;
   }</code>
</p>
<empty-line/>
<p>
А изменение самого параметра - запрещено:
</p>
<empty-line/>
<p>
<code>
   VOID &gt;&gt; (PCHAR Arg[][], INT Length)
   {
     Length ++;                                                                   /* !!! */
   }</code>
</p>
<empty-line/>
<p>
Это связано с тем, что иногда возникает потребность менять
числа по указателям с помощью оператора "*", вот так:
</p>
<empty-line/>
<p>
<code>
   *Pointer ++;</code>
</p>
<empty-line/>
<p>
И если случайно забыть его:
</p>
<empty-line/>
<p>
<code>
   Pointer ++;                                                                    /* ??.. */</code>
</p>
<empty-line/>
<p>
Значение переменной по указателю не будет изменено, а вместо
этого будет изменён сам указатель. Но больше такой ошибки не
повторится.
</p>
<empty-line/>
<p>
Данные по указателям тоже в большинстве случаев доступны на
чтение, а чтобы получить доступ к ним с правами на запись,
в заголовке функции надо задать флажок "WR":
</p>
<empty-line/>
<p>
<code>
   ULONG StrPos (PULONG WR Position, PCHAR String, PCHAR Token, ULONG Start_from = 0)
   {
     ...
   
     return Rc;
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   VOID Replace (PCHAR WR Target, ULONG Max_length, PCHAR Source, PCHAR Token, ULONG Start_from = 0)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   ULONG RC FScan (PCHAR WR Array[], ULONG Array_length,
                                     ULONG String_length,
                                     PCHAR Path, PCHAR Mask)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   ULONG VmAllocPages (PPVOID WR Pointer, ULONG Length, ULONG {VM_*} Flags = 0)
   {
     ...
   
     return Rc;
   }</code>
</p>
<empty-line/>
<p>
Во всех случаях сами параметры не могут быть изменены, но
флажки выборочно включают возможность изменения данных по
указателям. Здесь действует правило: данные, полученные с
правами на чтение, можно передавать другим функция только
с правами на чтение, а данные с правами на запись - могут
быть переданы другим функциям с правами как на чтение, так
и запись. Нет никакой возможности изменения данных, которые
не были переданы в функцию с правами на изменение.
</p>
<empty-line/>
<p>
Функции могут возвращать числа и флажки "1 или 0":
</p>
<empty-line/>
<p>
<code>
   INT Max (...)
   {
     ...
   }
   
   FLOAT Average (...)
   {
     ...
   }
   
   BYTE DoSomething (...)
   {
     ...
   
     return Rc;
   }</code>
</p>
<empty-line/>
<p>
Возврат указателей запрещён:
</p>
<empty-line/>
<p>
<code>
   PCHAR DoSomething (...)                                                        /* !!! */
   {
     ...
   
     return String;
   }</code>
</p>
<empty-line/>
<p>
Поскольку этот приём часто вызывал утечки памяти (из-за
потери указателей). Вместо возврата указателей на области
памяти надо заполнять внешние переменные - как в примерах
выше.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Глобальные и локальные переменные</p></title>
<p>
Глобальные переменные запрещены:
</p>
<empty-line/>
<p>
<code>
   ULONG X = 0;                                                                   /* !!! */
   
   VOID &gt;&gt; ()
   {
     stdio:Print ("%d", X);
   }</code>
</p>
<empty-line/>
<p>
И в данном случае надо написать так:
</p>
<empty-line/>
<p>
<code>
   VOID &gt;&gt; ()
   {
     ULONG X = 0;
   
     stdio:Print ("%d", X);
   }</code>
</p>
<empty-line/>
<p>
Также запрещены "поля в объектах" и тому подобное.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Расстановка пробелов и скобок</p></title>
<p>
В далёком прошлом, во времена работы с текстовыми терминалами
(и с электронно-лучевыми трубками) пробелы и скобки ставили по
правилам из книги "The C Programming Language":
</p>
<empty-line/>
<p>
<code>
   if (is_prime(number)) {                                                        /* K&#038;R C */
       printf("%d\n", number);
   } else {
       ;
   }</code>
</p>
<empty-line/>
<p>
Другим распространённым способом ставить их был "Berkeley
Style", где перед каждой фигурной скобкой добавляли переход
на новую строку:
</p>
<empty-line/>
<p>
<code>
   if (is_prime(number))                                                          /* Berkeley Style, K&#038;R C */
   {
     printf("%d\n", number);
   }
   else
   {
     ;
   }</code>
</p>
<empty-line/>
<p>
Здесь используется такой же стиль, но с пробелами перед круглыми
скобками:
</p>
<empty-line/>
<p>
<code>
   if (number-&gt;is_prime ())
   {
     print ("%d" "\n", number);
   }
   else
   {
     ;
   }</code>
</p>
<empty-line/>
<p>
И он же в случае, когда переменные и функции должны быть названы
строчными буквами:
</p>
<empty-line/>
<p>
<code>
   if (Number-&gt;IsPrime ())
   {
     Print ("%d" "\n", Number);
   }
   else
   {
     ;
   }</code>
</p>
<empty-line/>
<p>
Есть подозрения, что последний пример - самый лучший с точки
зрения поддержки программного кода (он же был принят в этой
книге в качестве единственного стандарта), и со шрифтами с
увеличенным расстоянием между строками (такими, как System
Monospaced в OS/2) он оказывается удобнее всех остальных. И
не забудем о большом количестве унаследованного кода с таких
языков, как Modula-2, Classic Rexx и другие.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Безымянные и внутренние функции</p></title>
<p>
Во многих случаях для того, чтобы программа стала более
правильной с точки зрения чтения и сопровождения, часть
функций располагается внутри других функций - вот так:
</p>
<empty-line/>
<p>
<code>
   FLOAT Hypotenuse (FLOAT A, FLOAT B)
   {
     FLOAT Square (FLOAT Z)
     {
       return Z * Z;
     }
   
     return Sqrt (Square (A) + Square (B));
   }</code>
</p>
<empty-line/>
<p>
Как видно, снаружи функцию "Square()" не видно: её никто
не будет вызывать, и она останется видна только внутри
функции "Hypotenuse()".
</p>
<empty-line/>
<p>
Другой похожий приём - безымянные функции, передаваемые
на вход другим функциям:
</p>
<empty-line/>
<p>
<code>
   FIXED64 Level = Read (&#038;Level, Settings, CS_LEVEL);
   
   INT N, Rc = NearestIn (&#038;N, Array, Length, (FIXED64 X) { // Level
     return Abs (X - Level);
   });</code>
</p>
<empty-line/>
<p>
Как видно, здесь функция "NearestIn()" заполняет внешнюю
переменную "N", которая передаётся ей по указателю:
</p>
<empty-line/>
<p>
<code>
   INT N, Rc = NearestIn (&#038;N, ...
       &#8593;                  &#8593;</code>
</p>
<empty-line/>
<p>
А получить доступ к каждому значению в массиве "Array" длины
"Length" ей помогает безымянная функция:
</p>
<empty-line/>
<p>
<code>
   INT N, Rc = NearestIn (&#038;N, Array, Length, (FIXED64 X) { // Level
                                             &#8593;             &#8593;</code>
</p>
<empty-line/>
<p>
Она получает на вход значение из массива в параметре "X"
и видит внешнюю переменную "Level" с доступом на чтение.
</p>
<empty-line/>
<p>
А в переменную "Rc" в этом примере возвращается единица или
ноль.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Проверяющие функции</p></title>
<p>
Как уже было показано в предыдущих главах, возможны функции,
отмеченные флажком "-&gt;[]", вот так:
</p>
<empty-line/>
<p>
<code>
   BYTE IsEven (INT N) -&gt; 1 | 0
   {
     return (N % 2 == 0) ? 1 : 0;
   }</code>
</p>
<empty-line/>
<p>
И с этим флажком их можно вызывать в форме "существительное,
глагол" - вот так:
</p>
<empty-line/>
<p>
<code>
   for (INT Counter = 0; Counter &lt; Length; Counter ++)
   {
     ...
   
     if (Counter-&gt;IsEven ())
     {
       ...
     }
   }</code>
</p>
<empty-line/>
<p>
Именно эта форма записи оказалась очень удобна в операторах
условия (именно так они и звучали бы в настоящих разговорных
языках).
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Служебные функции</p></title>
<p>
В том диалекте языка C, о котором рассказывает эта книга,
есть и ещё один запрет: нельзя размножать указатели между
областями данных - но можно копировать указатель в область
временных переменных, то есть в область стека. Такое же
правило действует и в отношении переменных с типом "VB",
позволяющих получить доступ к структурам данных в общей
"куче" (они позволяют получить указатель на данные, и
отличаются от указателей тем, что доступ к данным с их
помощью сопровождается проверкой существования данных).
</p>
<empty-line/>
<p>
Если же функция должна перемещать указатели в памяти, как
здесь:
</p>
<empty-line/>
<p>
<code>
   VOID RC SCPTR Reverse (P_TREE_NODE WR Tree_node)
   {
     ...                                                                          /* ... "SetAnchor()" ... */
   }</code>
</p>
<empty-line/>
<p>
Или здесь:
</p>
<empty-line/>
<p>
<code>
   VOID RC SCVB Reverse (P_TREE_NODE WR Tree_node)
   {
     ...                                                                          /* ... "SetAnchor()" ... */
   }</code>
</p>
<empty-line/>
<p>
Или здесь:
</p>
<empty-line/>
<p>
<code>
   VOID SCPTR Reverse (P_P_LIST_ITEM WR List)
   {
     ...                                                                          /* ... "SetAnchor()" ... */
   }</code>
</p>
<empty-line/>
<p>
Или здесь:
</p>
<empty-line/>
<p>
<code>
   VOID SCVB Reverse (P_P_LIST_ITEM WR List)
   {
     ...                                                                          /* ... "SetAnchor()" ... */
   }</code>
</p>
<empty-line/>
<p>
Эту возможность можно включить, добавив флажки "service function"
в её заголовок.
</p>
<empty-line/>
<p>
Особенность перемещения указателей состоит в том, что во время
создания области памяти, в том числе с помощью функций "Alloc()"
и "VmAllocPages()", в заголовке созданной области запоминается
адрес переменной с указателем на неё - что позволяет перемещать
её в памяти и менять этот указатель. И не допускает размножения
указателей на неё.
</p>
<empty-line/>
<p>
Так что все подобные действия надо выполнять по правилам -
и функции "Reverse()" в примерах выше будут, скорее всего,
вызывать какие-то служебные функции вроде "SetAnchor()" и
им подобные. А внутри них в любом случае будут выполнены
всевозможные проверки: что это за область памяти, существует
ли она, записан ли в её заголовке адрес переменной с указателем
на неё и так далее. Отключить эти проверки нельзя, но с ними
перемещение и копирование указателей становится безопасным.
</p>
<empty-line/>
<p>
Существует и возможность создавать ссылки на данные, увеличивая
счётчики ссылок - для этого есть функция "Alloc()" с параметром
"T_LINK" и другие.
</p>
</section><!-- 2 -->
</section><!-- 1 -->
<section><!-- 1 -->
<title><p>Условия и переключатели</p></title>
<p>
Операторы условия и переключатели - те же составные функции,
которые выполняют проверки (одна функция) и затем выполняют
одну из веток оператора условия или переключателя (остальные
функции). В том диалекте языка C, о котором рассказывает эта
книга, они должны быть заданы по правилам (о них - в следующих
главах).
</p>
<section><!-- 2 -->
<title><p>Обыкновенные операторы</p></title>
<p>
В самом простом случае оператор условия состоит из трёх
функций (одна выполняет проверку, другая - действие, третья -
сам оператор) и выглядит вот так:
</p>
<empty-line/>
<p>
<code>
   if (X &gt; 0)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Точно такой же оператор из четырёх функций выглядит так:
</p>
<empty-line/>
<p>
<code>
   if (X &gt; 0)
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Он же в краткой форме записи:
</p>
<empty-line/>
<p>
<code>
   if (X &gt; 0) ...
   else ...</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   if (X &gt; 0) ...</code>
</p>
<empty-line/>
<p>
Проверки в заголовке операторов условия могут быть соединены
в цепочку "ИЛИ":
</p>
<empty-line/>
<p>
<code>
   if (X &gt; 0 || Y &gt; 0)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И в таком случае он может иметь продолжение в виде "else":
</p>
<empty-line/>
<p>
<code>
   if (X &gt; 0 || Y &gt; 0)
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
А проверки, соединённые в цепочку "И":
</p>
<empty-line/>
<p>
<code>
   if (X &gt; 0 &#038;&#038; Y &gt; 0)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Не могут иметь продолжения в виде "else":
</p>
<empty-line/>
<p>
<code>
   if (X &gt; 0 &#038;&#038; Y &gt; 0)
   {
     ...
   }
   else                                                                           /* !!! */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Так что вместо условий с такими цепочками надо либо развернуть
их (получив из цепочки "И" точно такую же цепочку "ИЛИ"), либо
добавить внутреннюю функцию, где и выполнить все проверки.
</p>
<empty-line/>
<p>
В том случае, когда в заголовке оператора условия встречается
отрицание, оно должно применяться к переменной или выражению,
не включённому в какие-либо расчёты или вычисления и другие
проверки:
</p>
<empty-line/>
<p>
<code>
   if (!Number % 2)                                                               /* ??!.. */
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
А вот так - можно:
</p>
<empty-line/>
<p>
<code>
   if (!(Number % 2))
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   if (Number % 2 == 0)
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И так:
</p>
<empty-line/>
<p>
<code>
   if (Number % 2 != 0)
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Похожий пример:
</p>
<empty-line/>
<p>
<code>
   if (!Bits &#038; THIRD_BIT)                                                         /* ??!.. */
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Должен быть переписан вот так:
</p>
<empty-line/>
<p>
<code>
   if (!(Bits &#038; THIRD_BIT))
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
А следующий пример:
</p>
<empty-line/>
<p>
<code>
   if (!Result || !Error_code == 0)                                                  /* !!! */
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Лучше было бы переписать вот так:
</p>
<empty-line/>
<p>
<code>
   if (!Result || Error_code != 0)
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   if (!Result || !(Error_code == 0))
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   if (!Result || !Error_code-&gt;Eq (NO_ERROR))
   {
     ...
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И в каждом случае проверки в заголовках операторов условия
оказываются более читаемыми - даже в последнем случае, где
вместо проверок вызывается функция. Поддерживать такой код
гораздо проще, чем код с цепочками, где нет никаких правил,
и где легко допустить ошибку в правой части цепочки.
</p>
<empty-line/>
<p>
Операторы присваивания в заголовках операторов условия запрещены:
</p>
<empty-line/>
<p>
<code>
   if (Color = Current_color)                                                     /* ?!!.. */
   {
   }</code>
</p>
<empty-line/>
<p>
Ясно, что здесь имелось в виду:
</p>
<empty-line/>
<p>
<code>
   if (Color == Current_color)
   {
   }</code>
</p>
<empty-line/>
<p>
И так - тоже нельзя:
</p>
<empty-line/>
<p>
<code>
   while (Row = FetchNext (&#038;Row, Rs))                                             /* !!! */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И так - тоже:
</p>
<empty-line/>
<p>
<code>
   while ((Row = FetchNext (&#038;Row, Rs)) != NULL)                                   /* !!! */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
А всё дело в том, что операторы сравнения и присваивания не
имеют возвращаемых значений.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Краткая форма записи</p></title>
<p>
Проверки в краткой форме записи выглядят как несколько
переменных или значений, заданный через запятую. Например,
вместо цепочки из двух условий:
</p>
<empty-line/>
<p>
<code>
   if (Action == DO_LOGIN || Action == DO_LOGOUT)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Можно написать так:
</p>
<empty-line/>
<p>
<code>
   if (Action == DO_LOGIN, DO_LOGOUT)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
А вместо цепочки из трёх условий:
</p>
<empty-line/>
<p>
<code>
   if (CX &gt; 0 &#038;&#038; CY &gt; 0 &#038;&#038; CZ &gt; 0)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Можно написать вот так:
</p>
<empty-line/>
<p>
<code>
   if (CX, CY, CZ &gt; 0)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
В таких операторах гораздо труднее допустить опечатку или
ошибку: чем более краткой оказывается форма записи, тем её
легче читать и исправлять.
</p>
<empty-line/>
<p>
Похожий и тоже очень удобный приём - два оператора сравнения
вокруг одной переменной. Вместо длинной цепочки:
</p>
<empty-line/>
<p>
<code>
   if (A &lt; B &#038;&#038; B &lt; C) ...</code>
</p>
<empty-line/>
<p>
Может быть написана короткая, делающая в точности то же самое:
</p>
<empty-line/>
<p>
<code>
   if (A &lt; B &lt; C) ...</code>
</p>
<empty-line/>
<p>
И даже вызовы функций:
</p>
<empty-line/>
<p>
<code>
   if (B-&gt;Between (A, C)) ...                                                     /* ??.. */</code>
</p>
<empty-line/>
<p>
Иногда лучше было бы заменить двойным сравнением:
</p>
<empty-line/>
<p>
<code>
   if (A &lt;= B &lt;= (C - 1)) ...</code>
</p>
<empty-line/>
<p>
И тогда не будет вопросов по поводу границ, которые задают
операторы слева и справа.
</p>
<empty-line/>
<p>
И наконец, оператор с вопросительным знаком и двоеточием -
он должен быть задан со скобками:
</p>
<empty-line/>
<p>
<code>
   INT Max (INT X, Y)
   {
     return (X &gt; Y) ? X : Y;
   }</code>
</p>
<empty-line/>
<p>
Но не так:
</p>
<empty-line/>
<p>
<code>
   INT Max (INT X, Y)
   {
     return X &gt; Y ? X : Y;                                                        /* !!! */
   }</code>
</p>
<empty-line/>
<p>
Скобки в таком случае повторяют заголовок обыкновенного
оператора условия - и такая запись (тоже в виде четырёх
функций) гораздо легче читается.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Вложенные условия</p></title>
<p>
Во многих случаях операторы условия могут быть записаны
"лесенкой", где каждый оператор имеет продолжение в виде
"else" (чтобы не потерять ни одного условия), и некоторые
ветки операторов остаются пустыми:
</p>
<empty-line/>
<p>
<code>
   if (X == 0)
   {
     ...
   }
   else
   {
     if (X == 1)
     {
       ...
     }
     else
     {
       ;
     }
   }</code>
</p>
<empty-line/>
<p>
А форма записи в виде цепочек "if/elseif/elseif/else" -
запрещена:
</p>
<empty-line/>
<p>
<code>
   if (X == 0)
   {
     ...
   }
   else if (X == 1)                                                               /* !!! */
   {
     ...
   }
   else
   {
     ;
   }</code>
</p>
<empty-line/>
<p>
Не было найдено ни одной задачи, где от неё была бы хоть
какая-то польза, и где она не могла быть заменена на более
строгую (и более понятную при чтении) запись "лесенкой".
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Переключатели</p></title>
<p>
Операторы, позволяющие выполнить одно и только одно действие
в зависимости от одного или нескольких условий, задаются с
помощью ключевого слова "select", вот так:
</p>
<empty-line/>
<p>
<code>
   ULONG64 RC Factorial (ULONG64 N)
   {
     select (N)
     {
       case 0: return 1;
       case 1: return 1;
     }
     else
     {
       return N * Factorial (N - 1);
     }
   }</code>
</p>
<empty-line/>
<p>
И в более краткой форме записи:
</p>
<empty-line/>
<p>
<code>
   select (Month)
   {
     case 1, 3, 5, 7, 8, 10, 12: return 31;
     case 4, 6, 9, 11:           return 30;
     case 2:                     return 28;
   }
   else
   {
     Error (ERR_PARAMETER);
   }</code>
</p>
<empty-line/>
<p>
А можно и так:
</p>
<empty-line/>
<p>
<code>
   BYTE Between (FLOAT X, Left, Right) -&gt; 1 | 0
   {
     select ()
     {
       case Left &lt; X &lt; Right: return 1;
     }
     else
     {
       return 0;
     }
   }</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   BYTE Between (FLOAT X, Y, Left_X, Right_X, Left_Y, Right_Y) -&gt; 1 | 0
   {
     select ()
     {
       case Left_X &lt; X &lt; Right_X &#038;&#038;
            Left_Y &lt; Y &lt; Right_Y: return 1;
     }
     else
     {
       return 0;
     }
   }</code>
</p>
<empty-line/>
<p>
Переключатели с цепочками "И", как в последнем примере, хоть и
имеют продолжение в виде "else" (оно есть у всех переключателей),
должны возвращать из этой ветки нулевое значение, либо вызывать в
ней функцию "Error()".
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Последовательные проверки</p></title>
<p>
С помощью ещё двух ключевых слов, "with" и "without", в программе
можно включать и выключать одну и ту же флаговую переменную - вот
так:
</p>
<empty-line/>
<p>
<code>
   BYTE IsImage (PCHAR File_name, ULONG Length) -&gt; 1 | 0
   {
     BYTE WR Flag = 1;
   
     with (Flag)
     {
       if (...) Flag = 0;
       if (...) Flag = 0;
       ...
     }
   
     return Flag;
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   BYTE IsImage (PCHAR File_name, ULONG Length) -&gt; 1 | 0
   {
     BYTE WR Flag = 0;
   
     without (Flag)
     {
       if (...) Flag = 1;
       if (...) Flag = 1;
       ...
     }
   
     with (Flag)
     {
       if (...) Flag = 0;
       if (...) Flag = 0;
       ...
     }
   
     return Flag;
   }</code>
</p>
<empty-line/>
<p>
И в таком случае компилятор заменяет эти действия на переключатель,
который заполнит переменную и вернёт полученное значение.
</p>
<empty-line/>
<p>
Иногда это удобнее.
</p>
</section><!-- 2 -->
</section><!-- 1 -->
<section><!-- 1 -->
<title><p>Функции высшего порядка</p></title>
<p>
Так называют функции, предназначенные для того, чтобы передать им на
вход безымянные функции и дополнить их, получив алгоритмы отдельно, а
логику программы - отдельно на уровне выше.
</p>
<empty-line/>
<p>
Подсчёт статистики, просмотр дерева каталогов, поиск в массивах и
списках - всё это удобнее всего делать с помощью нескольких функций,
соединяя их так, чтобы одна из них могла вызывать несколько других.
</p>
<section><!-- 2 -->
<title><p>Массивы и списки</p></title>
<p>
Как известно, пройти массив чисел или структур данных можно с помощью
цикла со счётчиком, увеличивая его от нуля до длины массива. Именно
это и делается в следующем примере, где такой цикл располагается внутри
функции, которая получает в одном из параметров указатель на другую
функцию:
</p>
<empty-line/>
<p>
<code>
   VOID RecordsIn (STRUCT Array[], ULONG Length, ((PSTRUCT) VOID) FP Fp)
   {
     for (INT Cntr = 0; Cntr &lt; Length; Cntr ++)
     {
       Fp (&#038;Array[Cntr]);
     }
   }</code>
</p>
<empty-line/>
<p>
Теперь её можно вызвать - вот так:
</p>
<empty-line/>
<p>
<code>
   RecordsIn (Rows, array_length (Rows), (P_ITEM Row) { // WR Average
     with (Average)
     {
       .Price += Row-&gt;Price;
       .Weight += Row-&gt;Weight;
       .Capacity += Row-&gt;Capacity;
       ...
     }
   });</code>
</p>
<empty-line/>
<p>
Как видно, безымянная функция здесь получает доступ к переменной
"Average", объявленной вместе с ней. Это поведение задаётся флажком
"FP" в заголовке функции "RecordsIn()".
</p>
<empty-line/>
<p>
А в случае, когда структуры располагаются не в массиве, а в списке -
точно такое же действие будет выглядеть вот так:
</p>
<empty-line/>
<p>
<code>
   ItemsIn (List, (P_ITEM Row) { // WR Average
     with (Average, Row)
     {
       .Price += -&gt;Price;
       .Weight += -&gt;Weight;
       .Capacity += -&gt;Capacity;
       ...
     }
   });</code>
</p>
<empty-line/>
<p>
И такой же точно пример с массивом чисел:
</p>
<empty-line/>
<p>
<code>
   NumbersIn (Array, array_length (Array), (FLOAT Number) { // WR Average
     Average += Number;
   });</code>
</p>
<empty-line/>
<p>
Как видно, во всех случаях для просмотра массива или списка удобно
было бы вызвать функцию, дополненную безымянной функцией, и внутри
неё выполнять цикл со счётчиком или цикл с просмотром списка.
</p>
<empty-line/>
<p>
Снаружи его не видно.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Разделение по уровням</p></title>
<p>
Ещё один пример, где выполняется поиск в массиве структур или чисел -
найти ближайший или наиболее удалённый элемент. Здесь появляется так
много разнообразных действий (включая сравнение и присваивание), что
эти действия будет *очень* удобно спрятать внутри функции, вызывающей
безымянную функцию:
</p>
<empty-line/>
<p>
<code>
   #for FName, CC in { { NearestIn, &lt; }, { FurthermostIn, &gt; } } write
   
   #for T in { USHORT, INT, ULONG, INT64, ULONG64, FIXED, FIXED64 } write
   
   BYTE [FName] (PULONG WR Index, [T] Array[], ULONG Length, (([T]) [T]) FP Delta)
   {
     BYTE Run () // WR Index, Array, Length, Delta
     {
       [T] WR Previous = Delta (Array[0]); *Index = 0;
   
       NumbersIn (Array, Length, (INT I, [T] Number) { // WR Previous, WR Index, Delta
         [T] Current = Delta (Number);
   
         if (Current [CC] Previous)
         {
           Previous = Current; *Index = I;
         }
       });
   
       return 1;
     }
   
     return (Length &gt; 0) ? Run () : 0;
   }
   
   #end
   
   #end</code>
</p>
<empty-line/>
<p>
Здесь функция "NearestIn()" заполняет внешнюю переменную и возвращает
единицу или ноль. Кажется, что это что-то страшное, какая-то мешанина
символов, а следовательно - и нескончаемый источник ошибок... но вот
как она выглядит снаружи:
</p>
<empty-line/>
<p>
<code>
   FIXED Level = Read (&#038;Level, Settings, CS_LEVEL);
   
   INT N, Rc = NearestIn (&#038;N, Array, Length, (FIXED X) { // Level
     return Abs (X - Level);
   });</code>
</p>
<empty-line/>
<p>
Как видно, снаружи все действия укладываются в три строки. Такое вот
разделение по уровням.
</p>
<empty-line/>
<p>
Текст функций "NearestIn()" и "FurthermostIn()" написан с помощью
препроцессора, позволяющего не повторять названия в программе - так
что они будут работать с любыми числами, как с целыми, так и с точкой
в виде "делимое и делитель" (но не с числами в формате IEEE 754, не с
указателями и не со структурами данных - в таком случае их надо будет
повторить ещё несколько раз).
</p>
<empty-line/>
<p>
Когда-то, много лет назад, когда функции высшего порядка ещё не
были изобретены, поиск в массивах выполнялся с помощью циклов,
прямым перебором - и по сей день в таких языках, как C#, C++ и
Java, он выполняется почти так же... ну разве что вместо циклов
"for" сегодня пишут "foreach".
</p>
<empty-line/>
<p>
А ведь решение кажется очевидным... но нет.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Просмотр дерева каталогов</p></title>
<p>
В следующем примере функция "FScan()" просматривает дерево каталогов,
и для каждого найденного файла вызывает внешнюю функцию:
</p>
<empty-line/>
<p>
<code>
   VOID RC FScan (PCHAR Path, PCHAR Mask, FSCANPROC FP Fp)
   {
     VB Target, PCHAR Str_target = Alloc (&#038;Target, &#038;Str_target, T_STRING, Str_length, Path, "/", Mask);
   
     FilesIn (Str_target, (PCHAR Name) { // Mask, Fp
       if (Name-&gt;IsDirectory ())
         FScan (Name, Mask, Fp);
       else
         Fp (Name);
     });
   
     Free (&#038;Target, &#038;Str_target);
   }</code>
</p>
<empty-line/>
<p>
Кажется, что всё просто - но ничего подобного так и не появилось ни в
одном языке программирования.
</p>
<empty-line/>
<p>
Так и остаётся невыясненным, что сдерживает их.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Подсчёт частоты встречаемости слов в тесте</p></title>
<p>
А здесь функция "Frequency()" вместе с функцией "Calc()" вызывает функцию
"WordsIn()", чтобы подсчитать частоту, с которой каждое слово встречается
в тексте:
</p>
<empty-line/>
<p>
<code>
   VOID Frequency (VB WR Fq, VB Text)
   {
     VOID Calc () // WR Fq, Text
     {
       WordsIn (Text, (PCHAR Word) { // WR Fq
         ULONG WR Access_code = 0;
   
         if (!Fq-&gt;Contains (&#038;Access_code, Text, Word))
         {
           Add (Fq, Text, Word, 1);
         }
         else
         {
           ULONG Counter = Get (&#038;Counter, Fq, Access_code);
   
           Set (Fq, Access_code, Counter + 1);
         }
       });
     }
   
     if (Fq-&gt;Is (T_LIST, T_SLICE, T_ULONG)) Calc ();
     else Error (ERR_PARAMETER);
   }</code>
</p>
<empty-line/>
<p>
Если слово встретилось впервые, значение счётчика полагается равным единице,
а затем он увеличивается.
</p>
<empty-line/>
<p>
При этом переменная "Word" содержит указатели на строки - а не сами строки.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Статистика по объявлениям о работе</p></title>
<p>
Точно такие же приёмы подходят и для подсчёта статистики - например, по
объявлениям из сети Интернет. Как пример - объявления о работе:
</p>
<empty-line/>
<p>
<code>
   #define RS/FIELD_NAMES {                                                       /**
     No_Salary_in_Public_Offer,                                                    * Не указана заработная плата
     Contains_Interesting_Project,                                                 * Содержит "интересный проект"
     Contains_Young_Friendly_Collective,                                           * Содержит "молодой дружный коллектив"
     Contains_Willingness_Responsibility_Learningability,                          * Содержит "готовность, ответственность, обучаемость"
     Contains_Ability_to_Puzzle_Out_an_Alien_Code,                                 * Содержит "умение разбираться в чужом коде"
     Contains_Proven_Commercial_Experience,                                        * Содержит "подверждённый опыт коммерческой разработки"
     Too_Many_Roles_for_One_Employee,                                              * Слишком много ролей на одного сотрудника
     No_Email_or_Website_{DANGER!},                                                * Нет электронного адреса или Web-сайта (внимание, опасность!)
     No_Confirmation_that_Samples_Were_Reviewed,                                   * Не получено подверждение, что были просмотрены примеры (только отправить их - недостаточно)
     No_Confirmation_that_Webpage_Was_Reviewed,                                    * Не получено подверждение, что была просмотрена домашняя страница (только создать её - недостаточно)
     Given_an_Order_to_Send_a_Text_in_WinWord,                                     * Дан приказ отправить текст в WinWord
     Given_an_Order_to_Call_and_Beg_about_Job_Interview,                           * Дан приказ позвонить и попросить о собеседовании
     Given_an_Order_to_Rush_Tomorrow_at_10_00,                                     * Дан приказ явиться завтра в 10:00
     Received_a_Questionnaire_of_Self_Appraisal_of_a_Candidate,                    * Прислали "анкету самооценки кандидата"
     Received_a_Spit_in_a_Form_of_Test_Exercise,                                   * Плюнули тестовым заданием
     Testing_in_Office,                                                            * Тестируют в офисе
     Testing_on_a_Staircase,                                                       * Тестируют на лестничной клетке
     Recruitment_Agency_Instead_of_a_Customer,                                     * Кадровое агентство вместо фирмы-заказчика
     Recruitment_Agency_Hides_the_Name_of_a_Customer,                              * Кадровое агентство не называет, кто заказчик
     No_Responce_at_All                                                            * Ответ не был получен
   }                                                                               */
   
   struct RUSSIAN_STATISTICS
   {
     #for Field_name in RS/FIELD_NAMES write
   
     FIXED [Field_name];
   
     #end
   }
   
   typedef RUSSIAN_STATISTICS * P_RUSSIAN_STATISTICS;</code>
</p>
<empty-line/>
<p>
Как видно, здесь они были собраны в структуры данных (не без помощи
препроцессора), и теперь из них можно создать массив - и в такой же
структуре вести подсчёт среднего:
</p>
<empty-line/>
<p>
<code>
   RUSSIAN_STATISTICS Rows[] = {
                                 {
                                   ...
                                 },
                                 {
                                   ...
                                 },
   
                                 ...
   
                               };
   
   RUSSIAN_STATISTICS Average = {0};</code>
</p>
<empty-line/>
<p>
Сам же подсчёт среднего будет выглядеть так:
</p>
<empty-line/>
<p>
<code>
   VOID Calc (P_RUSSIAN_STATISTICS WR Average, RUSSIAN_STATISTICS Rows[], ULONG Array_length)
   {
     RecordsIn (Rows, Array_length, (P_RUSSIAN_STATISTICS Row) { // WR Average
   
       #for Field_name in RS/FIELD_NAMES write
   
       Average-&gt;[Field_name] += (Row-&gt;[Field_name]) ? 1 : 0;
   
       #end
   
     });
   
     #for Field_name in RS/FIELD_NAMES write
   
     Average-&gt;[Field_name] /= Length;
   
     #end
   }</code>
</p>
<empty-line/>
<p>
И теперь полученную функцию можно будет вызвать - вот так:
</p>
<empty-line/>
<p>
<code>
   Calc (&#038;Average, Rows, array_length (Rows));</code>
</p>
<empty-line/>
<p>
Как видно, здесь понадобился препроцессор (о нём рассказано в отдельной
главе), позволяющий не повторять названия полей в структурах. Для того,
чтобы перебрать структуры в массиве, вызывается функция "RecordsIn()".
Передаваемая ей на вход безымянная функция увеличивает счётчики, и затем
каждое поле в структуре "Average" делится на длину массива.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Массив структур данных в памяти</p></title>
<p>
И ещё один пример:
</p>
<empty-line/>
<p>
<code>
   # record-queue
   
   #include &lt;API.h&gt;
   
   #define R_* {
     EMPTY  0
     FILLED 1
   }
   
   struct CYCLICAL_BUFFER
   {
     struct QUEUE_RECORD
     {
       ULONG {R_*} Filled; PSTRUCT Pointer;
     }
   
     typedef QUEUE_RECORD :: T_*, TYPE_*;
     typedef QUEUE_RECORD * P_QUEUE_RECORD;
   
     P_QUEUE_RECORD Array; INT From, To, Array_length;
   }
   
   typedef CYCLICAL_BUFFER * P_CYCLICAL_BUFFER;
   
   VOID Create (P_CYCLICAL_BUFFER WR Record_queue, INT Length)
   {
     Init (Record_queue, Length);
   }
   
   VOID Reset (P_CYCLICAL_BUFFER WR Record_queue)
   {
     Init (Record_queue, Record_queue-&gt;Array_length);
   }
   
   VOID Init (P_CYCLICAL_BUFFER WR Record_queue, INT Length)
   {
     VOID ResetAll () // WR Record_queue
     {
       Record_queue-&gt;From, Record_queue-&gt;To = 0;
   
       if (Record_queue-&gt;Array)
       {
         for (INT Counter = 0; Counter &lt; Record_queue-&gt;Array_length; Counter ++)
         {
           if (Record_queue-&gt;Array[Counter].Pointer) Free (&#038;Record_queue-&gt;Array[Counter].Pointer);
         }
   
         Free (&#038;Record_queue-&gt;Array); Record_queue-&gt;Array_length = 0;
       }
     }
   
     ResetAll ();
   
     Record_queue-&gt;Array = Alloc (&#038;Record_queue-&gt;Array, T_ARRAY, T_STRUCT, T_QUEUE_RECORD, Length);
     Record_queue-&gt;Array_length = Length;
   }
   
   INT Length (P_CYCLICAL_BUFFER Record_queue)
   {
     return Abs (Record_queue-&gt;From - Record_queue-&gt;To);
   }
   
   BYTE IsEmpty (P_CYCLICAL_BUFFER Record_queue) -&gt; 1 | 0
   {
     return (Record_queue-&gt;From == Record_queue-&gt;To) ? 1 : 0;
   }
   
   VOID AddTo (P_CYCLICAL_BUFFER WR Record_queue, ULONG {T_*} Record_type, ((PSTRUCT WR) VOID) Fp)
   {
     VOID SendPointer () // WR Record_queue, Record_type
     {
       INT Index = Record_queue-&gt;To;
   
       Record_queue-&gt;Array[Index].Filled = R_FILLED;
   
       Record_queue-&gt;Array[Index].Pointer = Alloc (&#038;Record_queue-&gt;Array[Index].Pointer, T_STRUCT, Record_type);
   
       Fp (Record_queue-&gt;Array[Index].Pointer);
     }
   
     ULONG {ERR_*} SendPointer:* () // _
     {
       INT Index = Record_queue-&gt;To;
   
       return (Record_queue-&gt;Array[Index] != R_EMPTY) ? ERR_ARENA_TRASHED : 0;
     }
   
     if (Record_queue-&gt;From == Record_queue-&gt;To)
     {
       if (Record_queue-&gt;To == Record_queue-&gt;Array_length - 1)
       {
         Record_queue-&gt;To = 0; SendPointer ();
       }
       else
       {
         Record_queue-&gt;To ++; SendPointer ();
       }
     }
     else
     {
       if (Record_queue-&gt;From &lt; Record_queue-&gt;To)
       {
         if (Record_queue-&gt;To == Record_queue-&gt;Array_length - 1)
         {
           Record_queue-&gt;To = 0; SendPointer ();
         }
         else
         {
           Record_queue-&gt;To ++; SendPointer ();
         }
       }
       else
       {
         if (Record_queue-&gt;To == Record_queue-&gt;Array_length - 1)
         {
           Error (ERR_BUFFER_OVERFLOW);
         }
         else
         {
           Record_queue-&gt;To ++; SendPointer ();
         }
       }
     }
   }
   
   VOID TakeFrom (P_CYCLICAL_BUFFER WR Record_queue, ((PSTRUCT WR) VOID) Fp)
   {
     VOID SendPointer () // WR Record_queue
     {
       INT Index = Record_queue-&gt;From;
   
       Record_queue-&gt;Array[Index] = R_EMPTY;
   
       Fp (Record_queue-&gt;Array[Index].Pointer);
   
       Free (&#038;Record_queue-&gt;Array[Index].Pointer);
     }
   
     ULONG {ERR_*} SendPointer:* () // _
     {
       INT Index = Record_queue-&gt;From;
   
       return (Record_queue-&gt;Array[Index] != R_FILLED) ? ERR_ARENA_TRASHED : 0;
     }
   
     if (Record_queue-&gt;From == Record_queue-&gt;To)
     {
       if (Record_queue-&gt;From == Record_queue-&gt;Array_length - 1)
       {
         Error (ERR_BUFFER_OVERFLOW);
       }
       else
       {
         Error (ERR_BUFFER_OVERFLOW);
       }
     }
     else
     {
       if (Record_queue-&gt;From &lt; Record_queue-&gt;To)
       {
         if (Record_queue-&gt;From == Record_queue-&gt;Array_length - 1)
         {
           Error (ERR_BUFFER_OVERFLOW);
         }
         else
         {
           SendPointer (); Record_queue-&gt;From ++;
         }
       }
       else
       {
         if (Record_queue-&gt;From == Record_queue-&gt;Array_length - 1)
         {
           SendPointer (); Record_queue-&gt;From = 0;
         }
         else
         {
           SendPointer (); Record_queue-&gt;From ++;
         }
       }
     }
   }
   
   #export *, !Init</code>
</p>
<empty-line/>
<p>
И затем:
</p>
<empty-line/>
<p>
<code>
   #include &lt;API.h&gt;
   
   #import stdio
   
   #include "RecordQueue.="
   
   #import record-queue
   
   struct SALARY
   {
     PCHAR Title, Role, Status,
           Where, Topic, Headline;
     FLOAT Level;
   }
   
   typedef SALARY :: T_*, TYPE_*;
   typedef SALARY * P_SALARY;
   
   VOID &gt;&gt; ()
   {
     P_CYCLICAL_BUFFER Queue = Create (&#038;Queue, 10);
   
     AddTo (Queue, T_SALARY, (P_SALARY WR R) {
       with (R)
       {
         -&gt;Title    = Alloc (&#038;-&gt;Title,    T_STRING, "Кодерок");
         -&gt;Role     = Alloc (&#038;-&gt;Role,     T_STRING, "Игрунчик-развлеканец");
         -&gt;Status   = Alloc (&#038;-&gt;Status,   T_STRING, "Клоун среди пидарасов");
         -&gt;Where    = Alloc (&#038;-&gt;Where,    T_STRING, "Делис-Инфо бывш Пу-Пу Бызь");
         -&gt;Topic    = Alloc (&#038;-&gt;Topic,    T_STRING, "Молодой дружный коллектив интересных ребят, которые занимаются недвижимостью");
         -&gt;Headline = Alloc (&#038;-&gt;Headline, T_STRING, "У нас комфойтна! Мы следим за волосами в носу");
   
         -&gt;Level = 0;
       }
     });
   
     Print (Length (Queue));
   
     TakeFrom (Queue, (P_SALARY WR R) {
       with (R)
       {
         Print (-&gt;Level, "\t", -&gt;Role);
   
         Free (&#038;-&gt;Title, &#038;-&gt;Role, &#038;-&gt;Status,
               &#038;-&gt;Where, &#038;-&gt;Topic, &#038;-&gt;Headline);
       }
     });
   
     Print (Length (Queue));
   }</code>
</p>
</section><!-- 2 -->
</section><!-- 1 -->
<section><!-- 1 -->
<title><p>Рекурсия и циклы</p></title>
<p>
Рекурсивные вызовы и выполнение в циклах - целый огромный класс
задач, для которых понадобилось изобрести новые ключевые слова,
которых не было в языках программирования во времена ламповых и
транзисторных счётных машин. Сейчас это всё уже хорошо изучено -
и, как уже говорилось раньше, в данном диалекте языка C циклы и
рекурсивные вызовы работают как обыкновенные функции.
</p>
<empty-line/>
<p>
Это и есть функции.
</p>
<section><!-- 2 -->
<title><p>Деревья в памяти</p></title>
<p>
Один из наиболее простых и наглядных примеров, показывающих,
как выполняется рекурсия - задача "обойти дерево". Пусть в
памяти созданы структуры данных с указателями на другие такие
же структуры:
</p>
<empty-line/>
<p>
<code>
   struct RC WR TREE_NODE : RECORD                                                          &#183;&#183;&#183;         &#183;&#183;&#183;
   {                                                                                      /           /
     P_TREE_NODE Left;                                                            &gt;&gt; &#9472; **         &#183;&#183;&#183;
     P_TREE_NODE Right;                                                                   \     /
     RECORD Record;                                                                         &#183;&#183;&#183;         &#183;&#183;&#183;
   }                                                                                            \     /
                                                                                                  &#183;&#183;&#183;
   typedef TREE_NODE * P_TREE_NODE;                                                                   \
   typedef P_TREE_NODE * P_P_TREE_NODE;                                                                 &#183;&#183;&#183;</code>
</p>
<empty-line/>
<p>
И для того, чтобы обойти их и выбрать одну из них, достаточно
написать такую функцию:
</p>
<empty-line/>
<p>
<code>
   VOID RC Search (P_P_TREE_NODE WR Result, P_TREE_NODE Node, ((P_TREE_NODE) ULONG) FP IsDesired -&gt; 1 | 0)
   {
     VOID Walk (P_TREE_NODE Node) // WR Result, IsDesired
     {
       if (Node)
       {
         Search (Result, Node, IsDesired);
       }
     }
   
     with (Node)
     {
       if (-&gt;IsDesired ())
       {
         *Result = Node;
       }
   
       if (!*Result) Walk (-&gt;Left);
       if (!*Result) Walk (-&gt;Right);
     }
   }</code>
</p>
<empty-line/>
<p>
Как видно, эта функция проверяет каждый узел дерева, вызывая
для этого переданную ей на вход безымянную функцию, и в том
случае, когда она возвращает единицу - во внешнюю переменную
записывается указатель на узел дерева, после чего функция
возвращается из рекурсии (внешняя переменная с самого начала
должна содержать пустое значение).
</p>
<empty-line/>
<p>
А для того, чтобы функция могла войти в рекурсию, ей надо
задать флажок "RC" в заголовке.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Каталоги и файлы</p></title>
<p>
Точно такой же пример - просмотр дерева каталогов:
</p>
<empty-line/>
<p>
<code>
   VOID RC FScan (PCHAR Path, PCHAR Mask, FSCANPROC FP Fp)
   {
     VB Target, PCHAR Str_target = Alloc (&#038;Target, &#038;Str_target, T_STRING, Str_length, Path, "/", Mask);
   
     FilesIn (Str_target, (PCHAR Name) { // Mask, Fp
       if (Name-&gt;IsDirectory ())
         FScan (Name, Mask, Fp);
       else
         Fp (Name);
     });
   
     Free (&#038;Target, &#038;Str_target);
   }</code>
</p>
<empty-line/>
<p>
Здесь вход в рекурсию (то есть повторный вызов той же функции)
выполняется в том случае, когда во время перебора встречается
каталог - а для файлов вызывается внешняя безымянная функция.
</p>
<empty-line/>
<p>
И здесь возможность входа в рекурсию задаётся флажком "RC" в
заголовке.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Циклы со счётчиком</p></title>
<p>
Самая первая и наиболее удобная разновидность циклов -
как раз циклы со счётчиком (кто бы что ни говорил о том,
как они "устарели"). В самом простом случае такой цикл
выглядит вот так:
</p>
<empty-line/>
<p>
<code>
   for (INT Cntr = 0; Cntr &lt; Length; Cntr ++)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И со счётчиком в виде длинного 64-разрядного числа:
</p>
<empty-line/>
<p>
<code>
   for (INT64 Cntr = 0; Cntr &lt; Length; Cntr ++)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Числа с точкой в качестве счётчиков использовать нельзя:
</p>
<empty-line/>
<p>
<code>
   for (FLOAT Cntr = 0; Cntr &lt; Length; Cntr ++)                                   /* !!! */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И так - тоже нельзя:
</p>
<empty-line/>
<p>
<code>
   for (FIXED Cntr = 0; Cntr &lt; Length; Cntr ++)                                   /* !!! */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Счётчик может быть увеличен в последнем операторе условия
внутри цикла, если он увеличивается в заголовке цикла:
</p>
<empty-line/>
<p>
<code>
   for (INT WR I = 0; I &lt; Length; I ++)
   {
     ...
   
     if (Step_over) I += Step_over;
     else {}
   }</code>
</p>
<empty-line/>
<p>
И уменьшен - по тем же правилам:
</p>
<empty-line/>
<p>
<code>
   for (INT WR I = Length - 1; I &gt;= 0; I --)
   {
     ...
   
     if (Step_over) I -= Step_over;
     else break;
   }</code>
</p>
<empty-line/>
<p>
Вызывать функции в заголовках циклов со счётчиком нельзя:
</p>
<empty-line/>
<p>
<code>
   for (INT Cntr = 0; Cntr &lt; StrLen (String); Cntr ++)                            /* !!! */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И вместо этого их надо вызывать перед циклом:
</p>
<empty-line/>
<p>
<code>
   INT Length = StrLen (String);
   
   for (INT Cntr = 0; Cntr &lt; Length; Cntr ++)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
В последнем операторе условия внутри цикла может располагаться
оператор "break", позволяющий выйти из цикла:
</p>
<empty-line/>
<p>
<code>
   for (...)
   {
     ...
   
     if (...) break;
   }</code>
</p>
<empty-line/>
<p>
Он же без оператора условия - считается ошибкой:
</p>
<empty-line/>
<p>
<code>
   for (INT I = 0; I &lt; Max; I ++)
   {
     ...
   
     break;                                                                       /* !!! */
   }</code>
</p>
<empty-line/>
<p>
И здесь - та же ошибка:
</p>
<empty-line/>
<p>
<code>
   for (INT I = 0; I &lt; Max; I ++)
   {
     ...
   
     if (...) Rc = 0; break;                                                      /* !!! */
   }                  &#8593;</code>
</p>
<empty-line/>
<p>
Цепочки "ИЛИ" в заголовках операторов цикла запрещены:
</p>
<empty-line/>
<p>
<code>
   BYTE WR Continue_flag = 1;
   
   for (INT I = 0; I &lt; Limit || Continue_flag; I ++)                              /* !!! */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Цепочки "И" - тоже запрещены:
</p>
<empty-line/>
<p>
<code>
   BYTE WR Terminate_flag = 0;
   
   for (INT I = 0; I &lt; Limit &#038;&#038; !Terminate_flag; I ++)                            /* !!! */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И вместо них надо воспользоваться оператором "break", во так:
</p>
<empty-line/>
<p>
<code>
   BYTE WR Terminate_flag = 0;
   
   for (INT I = 0; I &lt; Limit; I ++)
   {
     ...
   
     if (Terminate_flag) break;
   }</code>
</p>
<empty-line/>
<p>
Команды продолжения цикла нет:
</p>
<empty-line/>
<p>
<code>
   for (...)
   {
     if (...) continue;                                                           /* !!! */
   
     ...
   }</code>
</p>
<empty-line/>
<p>
И не будет, а вместо неё внутри циклов должны располагаться
операторы условия:
</p>
<empty-line/>
<p>
<code>
   for (...)
   {
     if (...)
     {
       ...
     }
     else
     {
       ;
     }
   }</code>
</p>
<empty-line/>
<p>
Счётчик виден только внутри цикла, и во всех операторах, кроме
последнего оператора условия, он доступен только на чтение:
</p>
<empty-line/>
<p>
<code>
   FLOAT Average (FLOAT Array, INT Array_length)
   {
     FLOAT WR Total = 0;
   
     for (INT I = 0; I &lt; Array_length; I ++)
     {
       Total += Array[I];
     }
   
     return (I != 0) ? (Total / I) : 0;                                           /* !!! */
   }</code>
</p>
<empty-line/>
<p>
Так что в том случае, когда требуется где-то запоминать его -
надо создать внешнюю переменную и увеличивать её:
</p>
<empty-line/>
<p>
<code>
   FLOAT Average (FLOAT Array, INT Array_length)
   {
     FLOAT WR Total, INT WR Divider = 0;
   
     for (INT I = 0; I &lt; Array_length; I ++)
     {
       Total += Array[I]; Divider ++;
     }
   
     return (Divider != 0) ? (Total / Divider) : 0;
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   FLOAT Average (FLOAT Array, INT Array_length)
   {
     FLOAT WR Total, INT WR Divider = 0;
   
     for (INT I = 0; I &lt; Array_length; I, Divider ++)
     {
       Total += Array[I];
     }
   
     return (Divider != 0) ? (Total / Divider) : 0;
   }</code>
</p>
<empty-line/>
<p>
Но не так:
</p>
<empty-line/>
<p>
<code>
   FLOAT Average (FLOAT Array, INT Array_length)
   {
     FLOAT WR Total, INT WR Divider = 0;
   
     for (INT I = 0; I &lt; Array_length; I ++, Divider ++)                          /* !!! */
     {
       Total += Array[I];
     }
   
     return (Divider != 0) ? (Total / Divider) : 0;
   }</code>
</p>
<empty-line/>
<p>
Правила видимости счётчиков, как и запрет на некоторые операторы,
происходят от того, что циклы - те же функции. У них есть параметры,
и к ним должны применяться те же правила доступа, что и к параметрам
функций.
</p>
<empty-line/>
<p>
У циклов могут быть даже и операторы возврата.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Циклы с условием</p></title>
<p>
Вторая разновидность циклов - с условием: они выполняются
до тех пор, пока проверки в заголовке дают положительный
результат или вызываемая функция возвращает единицу, а не
нулевое значение:
</p>
<empty-line/>
<p>
<code>
   while (X &lt; Max_X &#038;&#038; Y &lt; Max_Y)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Такой цикл может не иметь условия в заголовке и завершаться
с помощью оператора "break":
</p>
<empty-line/>
<p>
<code>
   while ()
   {
     ...
   
     if (No_more_data) break;
   }</code>
</p>
<empty-line/>
<p>
При этом оператор выхода из цикла должен располагаться в
последнем операторе условия внутри цикла:
</p>
<empty-line/>
<p>
<code>
   while ()
   {
     ...
   
     if (No_more_data) break;
   
     ...                                                                          /* !!! */
   }</code>
</p>
<empty-line/>
<p>
И точно так же, для циклов со счётчиком:
</p>
<empty-line/>
<p>
<code>
   for (INT I = 0; I &lt; Array_length; I ++)
   {
     ...
   
     if (!Array[I]) break;
   
     ...                                                                          /* !!! */
   }</code>
</p>
<empty-line/>
<p>
Команды продолжения цикла нет:
</p>
<empty-line/>
<p>
<code>
   while (...)
   {
     if (...) continue;                                                           /* !!! */
   
     ...
   }</code>
</p>
<empty-line/>
<p>
И вместо неё должен быть написан оператор условия:
</p>
<empty-line/>
<p>
<code>
   while (...)
   {
     if (...)
     {
       ...
     }
     else
     {
       ;
     }
   }</code>
</p>
<empty-line/>
<p>
Любые цепочки с отрицанием в заголовках циклов считаются
ошибкой:
</p>
<empty-line/>
<p>
<code>
   while (!Terminate_flag_1 || !Terminate_flag_2)                                 /* ?!!.. */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И так - тоже нельзя:
</p>
<empty-line/>
<p>
<code>
   while (!Terminate_flag_1 &#038;&#038; !Terminate_flag_2)                                 /* ?!!.. */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Но можно написать так:
</p>
<empty-line/>
<p>
<code>
   while ()
   {
     ...
   
     if (Terminate_flag_1 || Terminate_flag_2) break;
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   while (Terminate_flag_1, Terminate_flag_2 == 0)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И тогда цикл завершится, как только будет установлен один из
двух флажков.
</p>
<empty-line/>
<p>
<code>
   while (Chars (Socket))
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Операторы присваивания в заголовках циклов должны быть
записаны по правилам - не так:
</p>
<empty-line/>
<p>
<code>
   while (Row = FetchNext (&#038;Row, Rs))                                             /* !!! */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
И не так:
</p>
<empty-line/>
<p>
<code>
   while ((Row = FetchNext (&#038;Row, Rs)) != NULL)                                   /* !!! */
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
А с помощью оператора условия и команды выхода из цикла -
вот так:
</p>
<empty-line/>
<p>
<code>
   while ()
   {
     PSTRUCT Row = FetchNext (&#038;Row, Rs));
   
     if (Row)
     {
       Process (Row);
     }
     else
     {
       break;
     }
   }</code>
</p>
<empty-line/>
<p>
А ещё лучше было бы вызывать функции вроде "RecordsIn()",
"ItemsIn()" или "RowsIn()".
</p>
<empty-line/>
<p>
И наконец, бесконечные циклы "while" без условия в заголовке
и без оператора условия с командой "break" - тоже запрещены.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Операторы возврата</p></title>
<p>
Циклы со счётчиком и оператором условия могут иметь оператор
возврата и продолжение в виде "else":
</p>
<empty-line/>
<p>
<code>
   BYTE CharacterExists (CHAR Array[], CHAR Character, INT Array_length)
   {
     for (INT Cntr = 0; Cntr &lt; Array_length; Cntr ++)
     {
       if (Array[Cntr] == Character) return 1;
     }
     else
     {
       return 0;
     }
   }</code>
</p>
<empty-line/>
<p>
А вот так - нельзя:
</p>
<empty-line/>
<p>
<code>
   BYTE CharacterExists (CHAR Array[], CHAR Character, INT Array_length)
   {
     for (INT Cntr = 0; Cntr &lt; Array_length; Cntr ++)
     {
       if (Array[Cntr] == Character) return 1;                                    /* !!! */
     }
   
     return 0;
   }</code>
</p>
<empty-line/>
<p>
Потому, что во втором случае цикл выполнял бы возврат из
середины функции, а он запрещён.
</p>
<empty-line/>
<p>
У циклов с условием не бывает операторов возврата.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Хвостовая рекурсия</p></title>
<p>
Ещё одна разновидность циклов с условием, которые записаны
в виде функции, входящей в повторный вызов без сохранения
адреса возврата в стеке вызовов - то есть, это рекурсия,
которая крутится "на месте", как цикл:
</p>
<empty-line/>
<p>
<code>
   VOID TC ReadMessages (P_PROGRAM_STATE WR State, HSOCKET Socket, MSGPROC ProcessMessage)
   {
     MSG Message = Read (&#038;Message, Socket);
   
     ProcessMessage (State, &#038;Message);
   
     if (Chars (Socket)) ReadMessages (State, Socket, ProcessMessage);
   }</code>
</p>
<empty-line/>
<p>
Снаружи эта функция вызывается так:
</p>
<empty-line/>
<p>
<code>
   ReadMessages (&#038;State, Socket, (State_ptr, Message_ptr) {
     ...
   });</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   ReadMessages (&#038;State, Socket, ProcessMessage);</code>
</p>
<empty-line/>
<p>
А вызываемая из неё функция выглядит так:
</p>
<empty-line/>
<p>
<code>
   VOID ProcessMessage (P_PROGRAM_STATE WR State, P_MSG Message)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Она, как видно, получает указатель на структуру с состоянием
программы (она так и называется: "program state") и указатель
на структуру с сообщением. Она вызывает другие функции, что в
данном случае не имеет значения.
</p>
<empty-line/>
<p>
Функции, исполняющие хвостовую рекурсию, должны иметь флажок
"TC" в заголовке.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Вложенные циклы</p></title>
<p>
В дополнение ко всему, что было сказано раньше - циклы внутри
циклов запрещены тоже:
</p>
<empty-line/>
<p>
<code>
   for (...)
   {
     for (...)                                                                    /* !!! */
     {
       ...
     }
   }</code>
</p>
<empty-line/>
<p>
Равно как и циклы внутри условий:
</p>
<empty-line/>
<p>
<code>
   if (...)
   {
     for (...)                                                                    /* !!! */
     {
       ...
     }
   }</code>
</p>
<empty-line/>
<p>
Это не мешает, поскольку есть возможность добавить функцию
внутри той же функции и запустить ещё один цикл внутри неё.
А для того, чтобы написать что-нибудь вроде:
</p>
<empty-line/>
<p>
<code>
   void transpose (float WR b[], float a[], int rows, int cols)                   /* K&#038;R C, Java... */
   {
     for (int i = 0; i &lt; rows; i ++)
     {
       for (int j = 0; j &lt; cols; j ++)
       {
         b[j][i] = a[i][j];
       }
     }
   }</code>
</p>
<empty-line/>
<p>
Есть циклы с двумя счётчиками:
</p>
<empty-line/>
<p>
<code>
   VOID Transpose (FLOAT WR B[], FLOAT A[], INT Rows, INT Cols)
   {
     for (INT I, J = 0; I &lt; Rows, J &lt; Cols; I, J ++)
     {
       B[J][I] = A[I][J];
     }
   }</code>
</p>
<empty-line/>
<p>
Которые к тому же оказываются ещё и более краткими.
</p>
</section><!-- 2 -->
</section><!-- 1 -->
<section><!-- 1 -->
<title><p>&nbsp;</p></title>
<p>&nbsp;</p>
</section><!-- 1 -->
<section><!-- 1 -->
<title><p>Дерево типов данных</p></title>
<p>
Типы данных в языках программирования позволяют задать множество
действий, которое будет выполняться с той или иной переменной, а
также её размер в памяти и правила доступа к ней. Все типы связаны
между собой, что позволяет получить правила преобразования чисел и
адресных переменных, они же правила преобразования данных из одного
типа в другой.
</p>
<section><!-- 2 -->
<title><p>Дерево типов</p></title>
<p>
Типы данных, разделённые на группы и собранные в виде дерева,
выглядят таким образом:
</p>
<empty-line/>
<p>
<code>
   Целые числа:                                                                        Значение по умолчанию:
     BYTE и CHAR     - 8 разрядов,  {  0      ..  2^8-1   }                                       0
     WORD и USHORT   - 16 разрядов, {  0      ..  2^16-1  }                                       0
     INT и LONG      - 32 разряда,  {  -2^31  ..  2^31-1  }                                       0
     DWORD и ULONG   - 32 разряда,  {  0      ..  2^32-1  }                                       0
     INT64 и LONG64  - 64 разряда,  {  -2^63  ..  2^63-1  }                                       0
     QWORD и ULONG64 - 64 разряда,  {  0      ..  2^64-1  }                                       0
   
   Дроби и числа с точкой:
     FIXED   - 32 разряда со знаком, "числитель;знаменатель"                                      0
     FIXED64 - 64 разряда со знаком, "числитель;знаменатель"                                      0
     FLOAT   - 32 разряда со знаком, формат IEEE754                                               0
     FLOAT64 - 64 разряда со знаком, формат IEEE754                                               0
   
   Указатели:
     PVOID  - 32 или 64 разряда, указатель на любой тип данных                                   NULL
     PPVOID - 32 или 64 разряда, указатель на переменную с указателем                            NULL
   
   Структуры:
     _R - структура, где вместо "_R" задано название типа                                        {0}
   
   Константы со значениями и без:
     ULONG {C_*} - где вместо "C_*" задано множество констант                                     0
   
   Ссылки на области памяти:
     VB - число для доступа к данным программы в "heap"-области                                   0
     HANDLE - число для доступа к системным данным в "heap"-области                               0
   
   Массивы:
     _T[] - массив, где вместо "_T" задано название типа                                     {}, {0}, ""</code>
</p>
<empty-line/>
<p>
И более подробная таблица для чисел - целых и с точкой:
</p>
<empty-line/>
<p>
<code>
          &#9474;  FLOAT64                 &#9474;  float64
          &#9474;  FLOAT                   &#9474;  float
          &#9474;  FIXED64                 &#9474;  fixed64
          &#9474;  FIXED                   &#9474;  fixed
   QWORD  &#9474;  INT64, ULONG64, LONG64  &#9474;  int64, ulong64, long64
   DWORD  &#9474;  INT, ULONG, LONG        &#9474;  int, ulong, long
   WORD   &#9474;  USHORT                  &#9474;  ushort
   BYTE   &#9474;  CHAR                    &#9474;  char</code>
</p>
<empty-line/>
<p>
Константы "T_*" задаются во время создания областей памяти:
</p>
<empty-line/>
<p>
<code>
   Константа:                             Тип данных:
   
   T_BYTE и T_CHAR                        Байт
   T_WORD и T_USHORT                      Короткое целое
   T_SIGNED_LONG                          Длинное целое со знаком
   T_DWORD и T_ULONG                      Длинное целое без знака
   T_SIGNED_LONG64                        Длинное 64-разрядное целое со знаком
   T_QWORD и T_ULONG64                    Длинное 64-разрядное целое без знака
   
   T_FIXED                                Короткая дробь
   T_FIXED64                              Длинная 64-разрядная дробь
   T_FLOAT                                Короткое число с точкой
   T_FLOAT64                              Длинное 64-рядрядное число с точкой
   
   T_CONST                                Константа
   T_STRUCT                               Структура данных
   T_STRING                               Строка в виде структуры string
   T_CHARS                                Строка в виде массива байтов с нулём
   
   T_ARRAY                                Массив
   T_LIST                                 Список
   
   T_LINK                                 Ссылка на уже существующие данные
   T_SLICE                                Подмножество ("срез") по уже существующим данным
   
   T_HSTR1SLC                             Срез по строке со значением "handle"
   T_RCRD1SLC                             Срез над структурой с незаполненными полями</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   Сочетание констант:                    Тип данных:
   
   T_ARRAY | T_BYTE                       Массив байтов
   T_ARRAY | T_ULONG                      Массив целых чисел
   ...
   
   T_LIST | T_SLICE                       Список "срезов" по данным (слова в строке)
   T_LIST | T_LIST | T_STRUCT             Список указателей на списки структур данных
   ...
   
   T_HSTR1SLC | T_PRT_NAME                Срез по строке со значением "port name" (то есть имя порта)
   T_HSTR1SLC | T_FSK_NAME                Срез по строке со значением "file handle" (то есть имя файла)
   ...
   
   T_RCRD1SLC | T_SALARY                  Срез по структуре типа "SALARY", где все поля имеют значения по умолчанию
   T_RCRD1SLC | T_RGB_COLOR               Срез по структуре типа "RGB_COLOR", где все поля имеют значения по умолчанию
   ...
   
   T_STRING_CHAIN | T_POINTERS            Строка, составленная из указателей на строки
   T_STRING_CHAIN | T_VBLOCKS             Строка, составленная из ссылок на строки
   ...</code>
</p>
<empty-line/>
<p>
Буквы и флажки "1 или 0" надо располагать в переменных типа
"CHAR" и "BYTE".
</p>
<empty-line/>
<p>
Для данных, поступающих в "сыром" виде, предназначены типы
"BYTE", "USHORT", "WORD", "DWORD" и "QWORD".
</p>
<empty-line/>
<p>
Ключевые слова "TRUE" и "FALSE" запрещены: вместо них надо
использовать константы или значения "1 или 0".
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Константы</p></title>
<p>
Для того, чтобы не размножать (и потом не искать) числа
и строки по всей программе, существуют константы, которые
могут быть объявлены с помощью команды "define":
</p>
<empty-line/>
<p>
<code>
   #define MAX_TITLE_LENGTH  1024</code>
</p>
<empty-line/>
<p>
И теперь можно написать так:
</p>
<empty-line/>
<p>
<code>
   StrCopy (Target_ptr, MAX_TITLE_LENGTH, Source_ptr);</code>
</p>
<empty-line/>
<p>
Возможны и константы без значений:
</p>
<empty-line/>
<p>
<code>
   #define FPU_ENABLED</code>
</p>
<empty-line/>
<p>
И теперь можно написать так:
</p>
<empty-line/>
<p>
<code>
   #ifdef FPU_ENABLED
   
   ...
   
   #else
   
   ...
   
   #endif</code>
</p>
<empty-line/>
<p>
Константы могут содержать символ "/" - и тогда они будут
разделены на множества:
</p>
<empty-line/>
<p>
<code>
   #define STR/CHARSET/UTF8
   
   #define ROW/MAX_LENGTH  200
   
   #define RGB/*_COLOR {
     RED   0x00FF0000
     GREEN 0x0000FF00
     BLUE  0x000000FF
     ...
   
   }</code>
</p>
<empty-line/>
<p>
И можно будет написать так:
</p>
<empty-line/>
<p>
<code>
   SetCodePage (Screen, STR/CHARSET/UTF8);</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   VB Row,
   P_ROW Row_ptr = Alloc (&#038;Row,
                          &#038;Row_ptr,
                          T_ARRAY, T_STRUCT, T_ROW, ROW/MAX_LENGTH);</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   struct WR PIXEL
   {
     LONG X, Y;
     ULONG {RGB/*_COLOR} Color;
   }</code>
</p>
<empty-line/>
<p>
В последнем примере константа объявлена как "ULONG {}",
из-за того, что она имеет значение, и для этого значения
будет занято 4 байта - то есть, это длинное целое число.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Целые числа</p></title>
<p>
Целые числа, со знаком или без знака, могут быть объявлены
таким образом:
</p>
<empty-line/>
<p>
<code>
   INT WR X = 0; X --;
   
   ULONG WR Y = 0; Y += 10;
   
   INT64 WR Z = Log2 (100); Z *= 1000;</code>
</p>
<empty-line/>
<p>
Тип "INT" часто используется в циклах со счётчиком:
</p>
<empty-line/>
<p>
<code>
   for (INT I = 0; I &lt; Length; I ++)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
А типы "xWORD" и "BYTE" - для составления и разбора сообщений
в оконной оболочке:
</p>
<empty-line/>
<p>
<code>
   WORD Key_code,
   BYTE Repeat_count,
   BYTE Scan_code = Decode (&#038;Key_code,
                            &#038;Repeat_count,
                            &#038;Scan_code, Msg-&gt;Mp1);
   
   if (Key_code &#038; KC_SHIFT)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
При этом для многих сообщений существуют структуры данных -
как в следующем примере:
</p>
<empty-line/>
<p>
<code>
   CHAR_MSG Keyboard_message = Decode (&#038;Keyboard_message, Msg);
   
   if (Keyboard_message.Kbd_state &#038; KC_SHIFT)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Короткое целое число может быть преобразовано в длинное
целое с помощью оператора присваивания:
</p>
<empty-line/>
<p>
<code>
   USHORT X = 0; ULONG Y = X;</code>
</p>
<empty-line/>
<p>
Обратное преобразование задаётся с помощью скобок:
</p>
<empty-line/>
<p>
<code>
   ULONG X = 0; USHORT Y = (USHORT) X;</code>
</p>
<empty-line/>
<p>
Целые числа и типы "xWORD" не требуют скобок:
</p>
<empty-line/>
<p>
<code>
   ULONG X = 0; DWORD Y = X;</code>
</p>
<empty-line/>
<p>
И здесь они тоже не требуются:
</p>
<empty-line/>
<p>
<code>
   DWORD X = 0; ULONG Y = X;</code>
</p>
<empty-line/>
<p>
И несколько замечаний насчёт целочисленных типов "INT",
"USHORT", "LONG" и "ULONG", с помощью которых могут быть
созданы переменные с числами со знаком и без знака:
</p>
<empty-line/>
<p>
&#9642;&#160;Счётчики циклов и отступы в массивах всегда остаются
без знака.
</p>
<p>
&#9642;&#160;Тип "ULONG" - требование создать число без знака и
проверять его.
</p>
<p>
&#9642;&#160;Тип "LONG" - требование создать число со знаком и
отключить проверки.
</p>
<p>
&#9642;&#160;Тип "USHORT" не имеет знака.
</p>
<empty-line/>
<p>
Таким образом, тип "INT" - это обобщённый тип "целое число",
удобный для вычислений: он может быть как со знаком, так и
без знака.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Числа с точкой</p></title>
<p>
Для чисел с точкой и знаком предназначены два типа,
"FIXED" и "FLOAT" - дробный и с плавающей точкой.
Первый позволяет хранить числа в виде двух чисел
"делимое;делитель" в точном представлении:
</p>
<empty-line/>
<p>
<code>
   FIXED X = 11 / 7;                                                              /* 11;7 */</code>
</p>
<empty-line/>
<p>
А второй предназначен для работы с числами с плавающей
точкой по стандарту IEEE754:
</p>
<empty-line/>
<p>
<code>
   FLOAT X = 11 / 7;                                                              /* 1.57143 */</code>
</p>
<empty-line/>
<p>
Для чисел дробного типа возможно точное сравнение:
</p>
<empty-line/>
<p>
<code>
   if (X == 11 / 7)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
А числа с плавающей точкой надо сравнивать вот так:
</p>
<empty-line/>
<p>
<code>
   if (X-&gt;Eq (11 / 7, FP_PRECISION, FP_DELTA))
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Некоторые операторы не применяются к числам с точкой:
</p>
<empty-line/>
<p>
<code>
   FLOAT WR X = 0;
   
   X += 1;
   X -= 1;
   
   X ++;                                                                          /* !!! */
   X --;                                                                          /* !!! */</code>
</p>
<empty-line/>
<p>
Но доступны для дробного типа:
</p>
<empty-line/>
<p>
<code>
   FIXED WR X = 0;
   
   X += 1;
   X -= 1;
   
   X ++;
   X --;</code>
</p>
<empty-line/>
<p>
Никакие числа с точкой не могут быть преобразованы в
целое число:
</p>
<empty-line/>
<p>
<code>
   ULONG N = (ULONG) Sqrt (2);                                                    /* !!! */</code>
</p>
<empty-line/>
<p>
И для этого надо вызывать функцию "Round()":
</p>
<empty-line/>
<p>
<code>
   ULONG N = Round (Sqrt (2));</code>
</p>
<empty-line/>
<p>
Причём функции округления до N-го знака после точки нет:
она может привести к потере точности во время вычислений.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Структуры</p></title>
<p>
Структуры предназначены для того, чтобы хранить данные,
которые изменяются вместе. Например, это может быть цвет
точки в виде трёх цветовых составляющих:
</p>
<empty-line/>
<p>
<code>
   struct WR RGB
   {
     BYTE _, Red, Green, Blue;
   }</code>
</p>
<empty-line/>
<p>
Или односвязный список, содержащий любые данные:
</p>
<empty-line/>
<p>
<code>
   struct RC WR LIST_ITEM : T
   {
     P_LIST_ITEM : T Next;
   
     T Data;
   }
   
   typedef LIST_ITEM * P_LIST_ITEM;</code>
</p>
<empty-line/>
<p>
Структуры данных таких типов (они называются обобщёнными
типами) созданы быть не могут - но могут быть созданы новые
типы на их основе:
</p>
<empty-line/>
<p>
<code>
   typedef LIST_ITEM PRICE_LIST_ITEM : PRICE;</code>
</p>
<empty-line/>
<p>
И указатели на них:
</p>
<empty-line/>
<p>
<code>
   typedef PRICE_LIST_ITEM * P_PRICE_LIST_ITEM;</code>
</p>
<empty-line/>
<p>
И такие структуры могут быть созданы:
</p>
<empty-line/>
<p>
<code>
   PRICE Item = {
                  ID = Item_ID,
                  Price = Item_price,
                  Price_WS = Item_price_{WS}
                };</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   PRICE_LIST_ITEM Item = {
                            Data = {
                                     ID = Item_ID,
                                     Price = Item_price,
                                     Price_WS = Item_price_{WS}
                                   }
                          };</code>
</p>
<empty-line/>
<p>
А если добавить к ним константы "T_*" и "TYPE_*":
</p>
<empty-line/>
<p>
<code>
   typedef PRICE_LIST_ITEM :: T_*, TYPE_*;</code>
</p>
<empty-line/>
<p>
Их можно будет создавать и в области данных:
</p>
<empty-line/>
<p>
<code>
   VB Item,
   P_PRICE_LIST_ITEM Item_ptr = Alloc (&#038;Item,
                                       &#038;Item_ptr,
                                       T_STRUCT, T_PRICE_LIST_ITEM);
   
   Item_ptr-&gt;{ Data.ID,
               Data.Price,
               Data.Price_WS } = { Item_ID,
                                   Item_price,
                                   Item_price_{WS} };</code>
</p>
<empty-line/>
<p>
Теперь в заголовок созданной области будет записана константа
"TYPE_PRICE_LIST_ITEM", и в дальнейшем тип этой области может
быть проверен с помощью функции "Is()", что иногда бывает очень
удобно.
</p>
<empty-line/>
<p>
Отдельно стоит вопрос приведения типов, когда в переменной
записан указатель на структуру. В одном случае приведение
типов будет возможно:
</p>
<empty-line/>
<p>
<code>
   P_LIST_ITEM Generic_ptr = Item_ptr;</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   P_PRICE_LIST_ITEM Item_ptr = (P_PRICE_LIST_ITEM) Generic_ptr;</code>
</p>
<empty-line/>
<p>
А в другом - нет:
</p>
<empty-line/>
<p>
<code>
   P_PRICE Price_ptr = Item_ptr;                                                  /* !!! */
   
   P_PRICE Price_ptr = (P_PRICE) Item_ptr;                                        /* !!! */</code>
</p>
<empty-line/>
<p>
И в таком случае надо будет написать так:
</p>
<empty-line/>
<p>
<code>
   P_PRICE Price_ptr = &#038;Item_ptr-&gt;Data;</code>
</p>
<empty-line/>
<p>
Но если бы основной структурный тип был описан таким образом,
что внутренняя структура располагалась бы в самом начале большой
структуры - то есть:
</p>
<empty-line/>
<p>
<code>
   struct RC WR LIST_ITEM : T
   {
     T Data;
   
     P_LIST_ITEM : T Next_item_pointer;
   }
   
   typedef LIST_ITEM * P_LIST_ITEM;</code>
</p>
<empty-line/>
<p>
И в описании типа было бы сказано, что с ними должны работать
операторы приведения типов:
</p>
<empty-line/>
<p>
<code>
   typedef LIST_ITEM PRICE_LIST_ITEM : PRICE [=];</code>
</p>
<empty-line/>
<p>
То можно было бы написать так:
</p>
<empty-line/>
<p>
<code>
   P_PRICE Price_ptr = Item_ptr;</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   P_PRICE_LIST_ITEM Item_ptr = (P_PRICE_LIST_ITEM) Price_ptr;</code>
</p>
<empty-line/>
<p>
А вместо обыкновенного приведения типов:
</p>
<empty-line/>
<p>
<code>
   P_LIST_ITEM Generic_ptr = Item_ptr;                                            /* !!! */
   
   P_LIST_ITEM Generic_ptr = (P_LIST_ITEM) Item_ptr;                              /* !!! */</code>
</p>
<empty-line/>
<p>
Надо было бы написать так:
</p>
<empty-line/>
<p>
<code>
   P_PRICE_LIST_ITEM Next_ptr = &#038;Item_ptr-&gt;Next_item_pointer;</code>
</p>
<empty-line/>
<p>
Причём если бы такая структура была создана в области данных
(и у неё была бы константа "TYPE_*"), для проверки типа можно
было бы использовать как функцию "HasType()", так и ещё одну -
"HasGenericType()".
</p>
<empty-line/>
<p>
Подобные связи между структурными типами бывают нужны редко,
но именно с их помощью удаётся создавать структуры, например
"область в окне текстового редактора" и "параграф, рисунок,
таблица". Вряд ли возможен ещё какой-нибудь способ их связать
между собой (разве что написать вместо них классы, как в ООП),
но команда "typedef" позволяет это сделать (хотя её параметры
далеко не очевидны).
</p>
<empty-line/>
<p>
Внутри структуры может быть объявлен массив произвольной
длины:
</p>
<empty-line/>
<p>
<code>
   struct STRING
   {
     ULONG Length;
     PCHAR Pointer, CHAR Chars[];
   }
   
   typedef STRING * PSTRING;</code>
</p>
<empty-line/>
<p>
И её надо будет задать во время создания структуры:
</p>
<empty-line/>
<p>
<code>
   STRING Target = {
                     Length = Target_length,
                     Chars[STR_TITLE_LENGTH] = ""
                   };</code>
</p>
<empty-line/>
<p>
Получить расстояние (отступ в байтах) до одного из полей
структуры можно так:
</p>
<empty-line/>
<p>
<code>
   INT Offset = field_offset (PRICE_LIST_ITEM, Data);
   
   INT Offset = field_offset (PRICE_LIST_ITEM, Data, Title);
   
   INT Offset = field_offset (PRICE_LIST_ITEM, Data, Title, Array_index);</code>
</p>
<empty-line/>
<p>
А создать указатель на поле структуры, зная расстояние до
него от начала структуры - вот так:
</p>
<empty-line/>
<p>
<code>
   PCHAR Pointer = pointer_to (&#038;Price_list_item, Offset);</code>
</p>
<empty-line/>
<p>
Для доступа к полям и массивам внутри структуры существует
оператор в виде точки:
</p>
<empty-line/>
<p>
<code>
   CHAR Character = String.Chars[Position];</code>
</p>
<empty-line/>
<p>
А для доступа по указателю - в виде стрелки:
</p>
<empty-line/>
<p>
<code>
   CHAR Character = String-&gt;Chars[Position];</code>
</p>
<empty-line/>
<p>
Точно так же, получение указателя:
</p>
<empty-line/>
<p>
<code>
   PCHAR Pointer = &#038;String.Chars[Position];</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   PCHAR Pointer = &#038;String-&gt;Chars[Position];</code>
</p>
<empty-line/>
<p>
Все структуры принадлежат к каким-либо пространствам имён, и
в большинстве случаев их поля можно менять только внутри того
же пространства имён, где они были объявлены. Возможность их
изменения в других пространствах имён задаётся флажком "WR".
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Массивы</p></title>
<p>
Массивами называются последовательности чисел, структур
или указателей, расположенных в памяти последовательно.
Массив может быть создан следующим образом:
</p>
<empty-line/>
<p>
<code>
   RGB Row[ROW/MAX_LENGTH] = {};</code>
</p>
<empty-line/>
<p>
И с заполнением его содержимого нулями:
</p>
<empty-line/>
<p>
<code>
   FIXED64 Numbers[ROW/MAX_LENGTH] = {0};</code>
</p>
<empty-line/>
<p>
Строка в виде массива символов создаётся так:
</p>
<empty-line/>
<p>
<code>
   CHAR String[STR/MAX_LENGTH] = "";</code>
</p>
<empty-line/>
<p>
А массив с длиной - вот так:
</p>
<empty-line/>
<p>
<code>
   INT Length, RGB Bitmap_line[BITMAP/MAX_WIDTH] = {};</code>
</p>
<empty-line/>
<p>
А можно и так:
</p>
<empty-line/>
<p>
<code>
   INT Width, Height, RGB Bitmap[BITMAP/MAX_WIDTH][BITMAP/MAX_HEIGHT] = {};</code>
</p>
<empty-line/>
<p>
Получить данные из массива можно с помощью прямоугольных
скобок и оператора присваивания:
</p>
<empty-line/>
<p>
<code>
   CHAR Symbol = String[Position];</code>
</p>
<empty-line/>
<p>
А изменить их - так:
</p>
<empty-line/>
<p>
<code>
   String[Position] = 'A';</code>
</p>
<empty-line/>
<p>
Получение указателя выглядит так:
</p>
<empty-line/>
<p>
<code>
   PCHAR Pointer = &#038;String[Position];</code>
</p>
<empty-line/>
<p>
А изменение по указателю - вот так:
</p>
<empty-line/>
<p>
<code>
   *Pointer = 'A';</code>
</p>
<empty-line/>
<p>
При этом указатель на первый элемент массива может быть
получен и с помощью присваивания:
</p>
<empty-line/>
<p>
<code>
   PCHAR Pointer = String;</code>
</p>
<empty-line/>
<p>
А вот обратное преобразование требует круглых скобок:
</p>
<empty-line/>
<p>
<code>
   CHAR String[] = (CHAR[]) Pointer;</code>
</p>
<empty-line/>
<p>
Это правило позволяет работать с массивами символов так,
как будто это переменные со строками:
</p>
<empty-line/>
<p>
<code>
   CHAR String[] = "ABC";
   
   INT Length = StrLen (String);</code>
</p>
<empty-line/>
<p>
И часто в них и записаны строки.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Разряды в числах</p></title>
<p>
Для того, чтобы включать разряды в целых числах, существует
оператор "ИЛИ":
</p>
<empty-line/>
<p>
<code>
   File_dialog.Flags = FDS_OPEN_DIALOG | FDS_CENTER;                              /* OR */
   
   if (Open_multiple) File_dialog.Flags |= FDS_MULTIPLESEL;                       /* OR */</code>
</p>
<empty-line/>
<p>
А для того, чтобы проверять их - оператор "И":
</p>
<empty-line/>
<p>
<code>
   if (File_dialog.Flags &#038; FDS_OPEN_DIALOG)                                       /* AND */
   {
     if (File_dialog.Flags &#038; FDS_CENTER)                                          /* AND */
     {
       ...
     }
     else
     {
       ...
     }
   }
   else
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Отдельные байты и короткие числа могут быть собраны в
длинное целое число с помощью функции "Encode()":
</p>
<empty-line/>
<p>
<code>
   Encode (&#038;Color, 0, Red, Green, Blue);                                          /* ARGB, Alpha = 0 */</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   Encode (&#038;Color, Blue, Green, Red, 0);                                          /* BGRA, Alpha = 0 */</code>
</p>
<empty-line/>
<p>
А для того, чтобы разобрать длинное целое число, есть
функция "Decode()":
</p>
<empty-line/>
<p>
<code>
   Decode (&#038;Alpha, &#038;Red, &#038;Green, &#038;Blue, Color);                                   /* ARGB */</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   Decode (&#038;Blue, &#038;Green, &#038;Red, &#038;Alpha, Color);                                   /* BGRA */</code>
</p>
<empty-line/>
<p>
Есть также функции для переключения разрядов, сдвига их
влево и вправо, подсчёта нулей и единиц в старшей части
числа, и так далее.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Операторы присваивания</p></title>
<p>
Одна переменная может быть объявлена таким образом:
</p>
<empty-line/>
<p>
<code>
   INT X = 0;</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   ULONG {ERR_*} Error_code = 0;</code>
</p>
<empty-line/>
<p>
Несколько переменных могут быть заданы через запятую:
</p>
<empty-line/>
<p>
<code>
   INT Offset, Length = 0;</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   BYTE Rc, INT Offset, Length = 0;</code>
</p>
<empty-line/>
<p>
И точно так же, через запятую, нескольким переменным может
быть присвоено одно и то же значение:
</p>
<empty-line/>
<p>
<code>
   Offset, Length = 0;</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   Bitmap[X][Y].{ Red, Green, Blue } = { 0xFF, 0, 0 };</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   Bitmap[X][Y]-&gt;{ Red, Green, Blue } = { 0xFF, 0, 0 };</code>
</p>
<empty-line/>
<p>
А присваивание по цепочке выполняется таким образом:
</p>
<empty-line/>
<p>
<code>
   Prev = Next = Prev + Next;
   
   Prev = Next = Prev;
   
   Prev = Next = 0;</code>
</p>
<empty-line/>
<p>
И хоть оно и выглядит странным, были найдены несколько
алгоритмов, где оно будет полезно (среди них - подсчёт
чисел Фибоначчи, выбор значений из списка парами и так
далее).
</p>
<empty-line/>
<p>
В других языках присваивание по цепочке часто имеет другой
смысл, а именно запись одного и того же значения в несколько
переменных. Чтобы избежать путаницы, компилятор имеет ключи,
позволяющие отключать присваивание по цепочке, чтобы убедиться
в том, что его нигде не осталось.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Размерность и границы</p></title>
<p>
Как уже было показано в многочисленных примерах в
других главах, команда "typedef" позволяет создавать
новые типы данных на основе уже существующих - то есть:
</p>
<empty-line/>
<p>
<code>
   typedef ULONG CAPACITY,
           CAPACITY * PCAPACITY;</code>
</p>
<empty-line/>
<p>
Она же позволяет создавать массивы, которые выглядят
как обыкновенные типы:
</p>
<empty-line/>
<p>
<code>
   typedef CHAR STR64 [64];</code>
</p>
<empty-line/>
<p>
Она же позволяет создавать типы для указателей на
функции:
</p>
<empty-line/>
<p>
<code>
   typedef VOID (*FPTR) (PCHAR, INT);</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   typedef ((PCHAR, INT) VOID) * FPTR;</code>
</p>
<empty-line/>
<p>
И создавать новые структурные типы:
</p>
<empty-line/>
<p>
<code>
   typedef LIST_ITEM PRICE_LIST_ITEM : PRICE;</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   typedef LIST_ITEM PRICE_LIST_ITEM : PRICE [=];</code>
</p>
<empty-line/>
<p>
И типы для указателей на них:
</p>
<empty-line/>
<p>
<code>
   typedef PRICE_LIST_ITEM * P_PRICE_LIST_ITEM;</code>
</p>
<empty-line/>
<p>
А также объявлять константы "T_*" и "TYPE_*", которые
нужны для создания областей памяти и могут располагаться
в структурах данных:
</p>
<empty-line/>
<p>
<code>
   typedef ULONG64 QWORD :: T_*, TYPE_*;</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   typedef PRICE_LIST_ITEM :: T_*, TYPE_*;</code>
</p>
<empty-line/>
<p>
С помощью этой же команды можно задать размерность числа
как физической величины:
</p>
<empty-line/>
<p>
<code>
   typedef FIXED64 SPEED {Km/H};</code>
</p>
<empty-line/>
<p>
А также границы "от..до":
</p>
<empty-line/>
<p>
<code>
   typedef FIXED64 SPEED {SP : FROM .. TO};</code>
</p>
<empty-line/>
<p>
А также размерность и границы:
</p>
<empty-line/>
<p>
<code>
   typedef FIXED64 SPEED {Km/H, SP : FROM .. TO};</code>
</p>
<empty-line/>
<p>
Теперь все изменения переменных с заданным типом
будут сопровождаться проверками, не вышло ли значение
переменной за допустимые границы, а сложение, вычитание
и другие действия с ними будут возможны только если их
размерности совпадают - за этим следит компилятор.
</p>
</section><!-- 2 -->
</section><!-- 1 -->
<section><!-- 1 -->
<title><p>Задания препроцессору</p></title>
<p>
Препроцессор - надстройка над компилятором, которая работает до
него (и поверх него) сразу после того, как текст программы был
разобран в первом приближении, и позволяет добавить в программы
константы, правила выравнивания, проверки параметров, ведение
логов, точки прерывания и многое другое, что было бы не так легко
сделать обыкновенными средствами языка.
</p>
<section><!-- 2 -->
<title><p>Условия и константы</p></title>
<p>
Для того, чтобы создать константу в программе, существует команда
"define":
</p>
<empty-line/>
<p>
<code>
   #define FPU_ENABLED</code>
</p>
<empty-line/>
<p>
С помощью команды "include" можно подключать файлы:
</p>
<empty-line/>
<p>
<code>
   #include &lt;Math.h&gt;</code>
</p>
<empty-line/>
<p>
Команда "import" позволяет подключать пространства имён:
</p>
<empty-line/>
<p>
<code>
   #import math</code>
</p>
<empty-line/>
<p>
А команда "export" позволяет объявлять функции в пространствах
имён как внешние, вызываемые из любого другого пространства имён:
</p>
<empty-line/>
<p>
<code>
   #export Sqrt</code>
</p>
<empty-line/>
<p>
С помощью команды "ifdef" можно добавить в программу строки,
если была задана константа:
</p>
<empty-line/>
<p>
<code>
   #ifdef FPU_ENABLED
   
   ...
   
   #else
   
   ...
   
   #endif</code>
</p>
<empty-line/>
<p>
А с помощью команды "ifndef" - добавить строки, если она не была
задана:
</p>
<empty-line/>
<p>
<code>
   #ifndef FPU_ENABLED
   
   ...
   
   #else
   
   ...
   
   #endif</code>
</p>
<empty-line/>
<p>
Команда "for" предназначена для того, чтобы добавлять строки в
цикле:
</p>
<empty-line/>
<p>
<code>
   #for Field_name in RS/FIELD_NAMES write
   
   Average.[Field_name] /= Length;
   
   #end</code>
</p>
<empty-line/>
<p>
С помощью команды "pack" могут быть задано выравнивание полей в
структурах данных:
</p>
<empty-line/>
<p>
<code>
   #|. +pack { DEV/MESSAGE/POWER,
               DEV/MESSAGE/VOLTAGE,
               DEV/MESSAGE/VIBRATION,
               DEV/MESSAGE/TEMPERATURE }, DT_BYTE</code>
</p>
<empty-line/>
<p>
Команда "debug" включает добавление проверок во время выполнения:
</p>
<empty-line/>
<p>
<code>
   #|. +debug { redirect_packet_to_ethernet_0,
                redirect_packet_to_ethernet_1,
                redirect_packet_to_ethernet_2,
                redirect_packet_to_ethernet_3 }</code>
</p>
<empty-line/>
<p>
А команда "inline" приводит к тому, что функция будет подставлена
в точку вызова:
</p>
<empty-line/>
<p>
<code>
   #|. +inline { strlen, strpos, strncpy }</code>
</p>
<empty-line/>
<p>
Причём последние две команды задаются и действуют только для функций,
не изменяющих внешнее состояние (то есть, они должны получать данные
и заполнять структуры и массивы, либо получать и возвращать значения -
но не изменять структуры с общими данными, такие, как "program state").
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Пространства имён</p></title>
<p>
Пространства имён позволяют собирать функции вместе, создавая
из них наборы функций - как в следующем примере:
</p>
<empty-line/>
<p>
<code>
   # math
   
   FLOAT Sqrt (FLOAT X)
   {
     ...
   }
   
   FLOAT64 Sqrt (FLOAT64 X)
   {
     ...
   }
   
   ULONG Eq (FLOAT X, FLOAT Standard, FLOAT Epsilon, FLOAT Fp_delta)
   {
     ...
   }
   
   ULONG IsNaN (FLOAT X)
   {
     ...
   }
   
   ...
   
   #export Eq, IsNaN,
           Exp, Ln, Pow, Sqrt,
           Sin, Cos, Tn, Ctn, Atn, Actn</code>
</p>
<empty-line/>
<p>
А затем подключать их:
</p>
<empty-line/>
<p>
<code>
   #include &lt;Stdio.h&gt;
   #include &lt;Math.h&gt;
   
   VOID &gt;&gt; ()
   {
     stdio:Print ("%f" "\n", math:Sqrt (2));
   }</code>
</p>
<empty-line/>
<p>
И вот так:
</p>
<empty-line/>
<p>
<code>
   #include &lt;Stdio.h&gt;
   #include &lt;Math.h&gt;
   
   #import math, stdio
   
   VOID &gt;&gt; ()
   {
     Print ("%f" "\n", Sqrt (2));
   }</code>
</p>
<empty-line/>
<p>
В том случае, когда названия функций совпадают - над ними могут
быть созданы переходники:
</p>
<empty-line/>
<p>
<code>
   StdStrLen = [stdlib] StrLen;
   
   INT StrLen (PSTRING String)
   {
     return String-&gt;Length;
   }</code>
</p>
<empty-line/>
<p>
А добавление переходников с помощью команды "define":
</p>
<empty-line/>
<p>
<code>
   # math
   &#183;
   &#183;
   &#183;
   
   #define Sqrt (X) \
         &#038; Sqrt ((X), __FILE__, __FUNCTION__, __LINE__)
   
   #export ...</code>
</p>
<empty-line/>
<p>
Позволяет добавлять в точку вызова функции всё, что может
пригодиться для отладки:
</p>
<empty-line/>
<p>
<code>
   #include &lt;Stdio.h&gt;
   #include &lt;Math.h&gt;
   
   #import math, stdio
   
   VOID &gt;&gt; ()
   {
     Print ("%f" \n, Sqrt (2, "program.=", "&gt;&gt;()", 8));
   }</code>
</p>
<empty-line/>
<p>
При этом все структуры данных, которые получают на вход внешние
функции, также считаются внешними. А остальные - внутренними.
</p>
<empty-line/>
<p>
Доступ к данным вне пространств имён возможен только на чтение -
то есть, можно создать структуру данных, например, для работы со
строками:
</p>
<empty-line/>
<p>
<code>
   STRING Title = {
                    Length = STR_TITLE_LENGTH,
                    Chars[STR_TITLE_LENGTH] = ""
                  };</code>
</p>
<empty-line/>
<p>
И читать её содержимое:
</p>
<empty-line/>
<p>
<code>
   INT Length = Title.Length;
   
   CHAR Character = Title.Chars[0];</code>
</p>
<empty-line/>
<p>
Но доступ на изменение считается ошибкой:
</p>
<empty-line/>
<p>
<code>
   Title.Length = 1;                                                              /* !!! */
   
   Title.Chars[0] = 'A';                                                          /* !!! */</code>
</p>
<empty-line/>
<p>
И вместо него надо воспользоваться функцией из того же пространства
имён:
</p>
<empty-line/>
<p>
<code>
   StrCopy (&#038;Title, array_length (Title.Chars), 'A')</code>
</p>
<empty-line/>
<p>
Или добавить к описанию структуры флажок "WR":
</p>
<empty-line/>
<p>
<code>
   struct WR RGB
   {
     BYTE _, Red, Green, Blue;
   }</code>
</p>
<empty-line/>
<p>
И тогда её можно будет менять где угодно:
</p>
<empty-line/>
<p>
<code>
   RGB Color = {0};
   
   Color.Red = 0xFF;</code>
</p>
<empty-line/>
<p>
В первом случае это правило кажется очевидным: в других пространствах
имён не видно, как устроена структура данных внутри - а у неё в конце
массива должен быть нулевой байт. Вызванная функция добавит его, а в
случае доступа на изменение он не будет добавлен.
</p>
<empty-line/>
<p>
Во втором случае это же правило начинает мешать - и флажок "WR" его
отменяет.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Точки прерывания</p></title>
<p>
Точки прерывания могут быть добавлены прямо в текст программы:
</p>
<empty-line/>
<p>
<code>
   #.</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   #. I "\t" Array[I]</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   #.. Items</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   #.. I "\t" Items[I]</code>
</p>
<empty-line/>
<p>
Так можно выводить на печать любые переменные: как числа и строки,
так и структуры данных и массивы (в том числе и массивы со строками
и структурами данных).
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Ассемблерные вставки</p></title>
<p>
Вставки на языке Ассемблера выглядят следующим образом:
</p>
<empty-line/>
<p>
<code>
   VOID PortIn (PULONG WR Target, USHORT Port_number)
   {
     asm () // WR Target, Port_number / R0, R24
     {
       ...
     }
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   VOID Reverse (PULONG WR Target, ULONG Source)
   {
     asm (X86/64) // WR Target, Source / EAX, EBX, ECX
     {
       ...
     }
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   VOID Reverse (PULONG WR Target, ULONG Source)
   {
     asm (POWER/64) // WR Target, Source / R1, R2, R3
     {
       ...
     }
   }</code>
</p>
<empty-line/>
<p>
И теперь с помощью команды "include":
</p>
<empty-line/>
<p>
<code>
   #if TARGET_CPU == X86/64
   #include "X86_64.="
   #endif
   
   #if TARGET_CPU == POWER/64
   #include "POWER_64.="
   #endif</code>
</p>
<empty-line/>
<p>
Можно подключить один из файлов, где содержится тот код на
языке Ассемблера, который нужен для запуска программы.
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Регрессионные тесты</p></title>
<p>
Тесты на обнаружение ошибок (то есть "регрессии" программы)
выглядят следующим образом:
</p>
<empty-line/>
<p>
<code>
   #import math, regression
   
   VOID &gt;&gt; ()
   {
     [EXPECT] Max "returns X when X &gt; Y" () {
                                              ULONG X = Random (2^8, 2^16);
                                              ULONG Y = Random (0, 2^8 - 1);
   
                                              if (Max (X, Y) == X) return 1;
                                              else return 0;
                                            };
   
     [EXPECT] Max "returns Y when Y &gt; X" () {
                                              ULONG X = Random (0, 2^8 - 1);
                                              ULONG Y = Random (2^8, 2^16);
   
                                              if (Max (X, Y) == Y) return 1;
                                              else return 0;
                                            };
     &#183;
     &#183;
     &#183;
   }</code>
</p>
<empty-line/>
<p>
Или так:
</p>
<empty-line/>
<p>
<code>
   #import math, regression
   
   VOID &gt;&gt; ()
   {
     [EXPECT] Max ERR_PARAMETER "when X &lt; 0" () {
                                                  DWORD X = 2^32;
                                                  DWORD Y = 1;
   
                                                  ULONG Z = Max (X, Y);
                                                };
   
     [EXPECT] Max ERR_PARAMETER "when Y &lt; 0" () {
                                                  DWORD X = 1;
                                                  DWORD Y = 2^32;
   
                                                  ULONG Z = Max (X, Y);
                                                };
     &#183;
     &#183;
     &#183;
   }</code>
</p>
<empty-line/>
<p>
Как видно, для запуска тестов применяется макро-команда, а
сами тесты выглядят как безымянные функции. Они имеют другую
форму записи и у них нет доступа ко внешним переменным - зато
их можно запускать одновременно (что и делается).
</p>
</section><!-- 2 -->
<section><!-- 2 -->
<title><p>Ведение логов</p></title>
<p>
Для того, чтобы вести логи, сбрасывая куда-либо строки
с текстовыми сообщениями о том, как работает программа, к
функциям должны быть добавлены служебные функции. Например,
функция для выбора сообщений из очереди:
</p>
<empty-line/>
<p>
<code>
   ULONG ProcessMessage (P_THREAD_STATE WR State, P_RECEIVED_MESSAGE Message)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
Может быть дополнена функцией, вызываемой перед тем, как
она будет вызвана:
</p>
<empty-line/>
<p>
<code>
   VOID ProcessMessage:[&gt;&gt;] (PCHAR File_name, PCHAR Caller_name,
                             P_THREAD_STATE State, P_RECEIVED_MESSAGE Message)
   {
     ULONG String_ID = ResolveConstName (&#038;String_ID, Message-&gt;ID);
   
     Log (+1, "Entering %s (%s), id: %s" "\n", File_name, Caller_name, String_ID);
   }</code>
</p>
<empty-line/>
<p>
А также функцией, вызываемой после возврата из неё:
</p>
<empty-line/>
<p>
<code>
   VOID Process_message:[&lt;&lt;] (PCHAR File_name, PCHAR Caller_name,
                              P_THREAD_STATE State, P_RECEIVED_MESSAGE Message, ULONG Rc)
   {
     Log (-1, "Leaving %s (%s), rc: %d" "\n", File_name, Caller_name, Rc);
   }</code>
</p>
<empty-line/>
<p>
Входящие параметры можно проверить таким образом:
</p>
<empty-line/>
<p>
<code>
   ULONG {ERR_*} ProcessMessage:Message (P_THREAD_STATE State, P_RECEIVED_MESSAGE Message)
   {
     ...
   }
   
   ULONG {ERR_*} ProcessMessage:State (P_THREAD_STATE State, P_RECEIVED_MESSAGE Message)
   {
     ;
   }</code>
</p>
<empty-line/>
<p>
А возвращаемое значение проверяется так:
</p>
<empty-line/>
<p>
<code>
   ULONG {ERR_*} ProcessMessage:_ (P_THREAD_STATE State, P_RECEIVED_MESSAGE Message, ULONG Rc)
   {
     ...
   }</code>
</p>
<empty-line/>
<p>
В точке вызова функции ничего не изменится:
</p>
<empty-line/>
<p>
<code>
   VOID Messages (P_THREAD_STATE WR State, HSOCKET Socket, MSGPROC ProcessMessage)
   {
     while (Chars (Socket))
     {
       MSG Message = Read (&#038;Message, Socket);
   
       ProcessMessage (State, &#038;Message);
     }
   }</code>
</p>
<empty-line/>
<p>
Но препроцессор подставит в точку вызова этой функции другую,
искусственно созданную функцию, и она вызовет её и все остальные
функции.
</p>
</section><!-- 2 -->
</section><!-- 1 -->
<section><!-- 1 -->
<title><p>&nbsp;</p></title>
<p>&nbsp;</p>
</section><!-- 1 -->
<section><!-- 1 -->
<title><p>Последняя страница</p></title>
<empty-line/>
<image xlink:href="#PixelArt@Conclusion"/>
<empty-line/>
</section><!-- 1 -->

<!-- -#- -->
</body>
<binary content-type="image/png" id="BookCover">[encoded data, 9996 characters]</binary>
<binary content-type="image/png" id="PixelArt@Conclusion">[encoded data, 34748 characters]</binary>
</FictionBook>