Справочник по системным программам для компьютера ZX Spectrum 1991 г.

Описание TEST-PROG, HISOFT C - тестовая программа TEST-PROG. Язык программирования 'C'.


■ TEST-PROG

Тестовая программа, позволяющая проверять работоспособ-
ность компьютеров SINCLAIR ZX-SPECTRUM любых конфигура-
ций: 16К, 48К и 128К.

Программа выполняет проверки:

Клавиатуры; цвета, яркости, мигания; звука; ULA;

ПЗУ и оперативной памяти;

Работы с магнитофоном.

Все эти проверки могут быть выполнены по отдельности или
комплексно.

■ ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ С

1. Введение в систему программирования Си.

Приведем первый пример:

main ()

{printf ("First program");

};

После набора программы Вы должны отметить ее конец (EOF),
нажав SS+1, после чего появится сообщение:

TYPE Y ТО RUN

К этому моменту программа будет скомпилирована и ответ Y
приведет к выполнению программы!!!

Произведя все вышеуказанные манипуляции, Вы, возможно,
увидите на экране:

FIRST PROGRAM

TYPE Y TO RUN:

Если вместо Y вы нажмете любую другую клавишу, то система
HISOFT перейдет в изначальное состояние ввода текста программы.

Описанный способ компиляции используется только для компи-
ляции простых программ. Значительная же часть программ подготав-
ливается сначала РЕДАКТОРОМ и только после этого компилирует-
ся.

Если система Hisoft находится в режиме компилирования, то для
вызова редактора нажмите EDIT (CS+1), а затем ENTER. При этом
на экране появится ">". Каждая строка в режиме редактирования
нумеруется. Последовательность ввода номеров роли не играет; ре-
дактор сам расположит их в порядке возрастания.

Процесс замены или удаления строки с каким-либо номером
аналогичен замене и удалению в Basic. Для получения листинга
наберите директиву L.

После того, как Вы отредактировали исходный текст, его можно
скомпилировать, набрав директиву С. После вызова компилятора
необходимо использовать специальную директиву #include - ее ин-
терпретация приведет к ее замене исходным текстом компилируемой
программы и непосредственной компиляции программы. Если про-
грамма не имеет ошибок, то после этих манипуляций нажмите SS+1
(EOF).

Если после компиляции Вы вернетесь в редактор и введете ди-
рективу L, то Вы опять увидите исходный текст программы, следова-
тельно, он не удаляется при компиляции. Для набора новой
программы необходимо старую удалить директивой DM, N - где M,N

- строки программы.

Пример:

200 main ()
210 {

220 printf (Second program");
230 }

После ввода директив С и #include компилятор выдаст Вам со-
общение: ERROR 37 - неопределенная переменная, в данном случае

- отсутствие первых кавычек ". Если после этого вызвать директиву
Q, то на экране Вы получите строку с ошибкой.

2. Структура программ.

В системе Hisoft зарезервированны следующие ключевые слова:

auto

else

long

typedef

break

entry

register

union

case

extern

return

unsigned

chare

float

short

while

continue

for

sizeof

default

goto

static

do

if

struct

fortran

double

int

switch

asm

Кроме того: inline и cast.

Идентификаторами в С являются отличные от ключевых слов
последовательности цифр и букв, начинающиеся с букв. Большие и
малые буквы распознаются как разные идентификаторы. Распозна-
ваемая длина идентификатора равна 8 знакам.

Литералами называются числовые (числа), символьные (симво-
лы) и строковые константы. Числовой литерал состоит из последова-
тельности десятичных цифр.

Символьный литерал имеет вид "С", где С является символом,
отличным от " и , или описанием символа ( - описание символа с
кодом равным нулю.

п - описание символа "новая строка",.
г - описание символа "возврат каретки",
t - табуляция,
" - апостроф,
\ - наклонная черта).

Строчный литерал имеет вид "s", где s - любая последователь-
ность символов или их описаний. Строковый литерал представляет
собой ссылку на первый символ последовательности символов в апо-
строфах, дополненную символом с кодом 0 в качестве ограничителя
этой последовательности.

В отличие от других языков, символьный литерал не является
частным случаем строкового. Так "J" представляет собой односим-
вольное данное, которое имеет величину, равную коду буквы J, в то
время как "J" представляет собой данное, указывающее на первый
символ последовательности символов, состоящей из знака J и символа
с кодом 0.

Таким образом пример:

int Age=44; /*Для 1987 года*/

main ()
{

printfC'Jan %с is %d" %s"", "В", Age, "now");
}

содержит коментарий /*...*/, ключевое слово int, идентифика-
торы Age, main и printf, символьный литерал "В", строковый литерал
"now", оператор = и т.д. В итоге получим JanB is 44 "now".

3. Описание переменных, функций и типов.

Данные могут быть переменными и константами и представля-
ются своими именами. Простейшими именами констант являются
литералы, а переменных - идентификаторы. В общем случае данные
могут представляться выражениями.

Тип данных, представленных литералами, следует из самой за-
писи литералов, следовательно описание литералов излишне. Пере-
менные же должны быть обязательно описаны перед их
использованием в программе.

Описание (декларация) имеет вид:
определение-типа список-деклараторов;

Например:
int а, Ь, с.

Список деклараторов может иметь и более сложный вид:
int*(*(*fun())[2]().

Правила конструирования декрараторов следующие:

Если некоторая запись Dec является декларатором, то деклара-
торами также являются записи: (Dec), означающая то же самое, что
и Dec; Dec [w], означающая массив из w элементов; *Dec, означаю-
щая указатель на объект и Dec О, означающая функцию. Наивысшим
приоритетом обладают круглые скобки, отделяющие сам декларатор,
а наинизшим - знак *.

Пример:

int (*NameO) [2 ] равносильно int (*(Name())) [2 ];

- декларация функции Name, результатом которой является
указатель двухэлементного массива данных типа (int). Ошибочной
декларацией является: char Fun()[3], эквивалентная char
(Fun ()) [3 ], т.к. она объявляет функцию, результатом которой явля-
ется трехэлементный массив данных типа (char), а результатом фун-
кции может быть только скалярная величина.

Остается отметить, что если некоторый объект программы объ-
явлен с помощью декларации: определение - типа декларатор, то он
принимается равным определению: (определение - типа псевдодек-
ларатор), в котором декларатор отличается от псевдодекларатора
тем, что последний не содержит идентификатора объекта.

Пример:

Переменная Ptr, объявленная как
int(*Ptr) [2 ] [3 ], имеет тип (int(*) [2 ] [3 ])

Объявление простых переменных.

# переменные типа (char) могут принимать значение 0...255;

# переменные типа (int) могут принимать значения - 32768...32767;

# переменные типа (uns igned) могут принимать значения
0...65535;

# указывающие переменные типа (char) могут принимать значе-
ния указателя на переменную.

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

Внимание: В данной реализации языка С присваивание на-
чальных данных допускается только во внешних по отношению к
определению функции декларациях:

char Initial-"J",

Name = "В";

int Age = 44;

mainO

{

int Year;
Year =1987;

printf("%c%c is %d in %d",Initial, Name, Age, Year);

}

Описание и присвоение значения переменной Year реализованы
разными операторами (т.к. она определяется внутри функции), а
переменной Age - одним.

Если переменной начальные данные не присваиваются, она при-
нимает значение 0.

Рассмотрим указывающие переменные. Ими являются данные,
определяющие положение других данных. Для преобразования вы-
ражений, представляющих определенные данные, в выражения,
представляющие указатели на эти данные, используется &. Обрат-
ным оператором является *.

К примеру, так как строковый литерал представляет данное,
указывающее на первый знак последовательности, определенной
этим литералом (литерал "JB" представляет данное, указывающее на
знак J), то выражение *"JB" представляет знак J последовательности,
состоящей из знаков J, В и знака с кодом ноль.

В С в выражениях с указателями допускаются операции сложе-
ния и вычитания. Так, если Ptr есть выражение, указывающее на i-ый
элемент последовательности данных, то Ptr + Num, где Num - целое,
указывает на i + Num элемент этой последовательности.

Prt + Num) эквивалентно Ptr [Num ] или Num [Ptr ].

Пару скобок [ ] в дальнейшем будем называть оператором ин-
дексирования.

Пример:

char *Ref="Jan";

main()

{

printf("%s=%c%c%c", Ref,*Ref, *<Ref+l),Ref [2]);

}

В этой программеRef объявлена как переменная, которой можно
поставить в соответствие указатель данных типа (char). Т.о., пере-
менная, представленная в программе идентификатором Ref, имеет

тип (char *). При объявлении переменной Ref присвоено значение,
указывающее на букву J 4-хзнаковой последовательности J,a,n и
знак с кодом ноль. В процессе выполнения команды, в которой вызыв-
вается функция printf, происходит связывание шаблона %s с указа-
телем, присвоенным переменной Ref.

Таким образом на экран выводется последовательность знаков,
йачиная с указанного места, до знака с кодом ноль, исключая послед-
ний, т.е. слова Jan. Если Ref представляет указатель на литеру J, то
*Ref представляет саму букву J. Следовательно, связывание первого
шаблона % с аргументом *Ref приведет к печати буквы J. Ref + 1
представляет указатель на букву a, a*(Ref + 1) - букву а. Выражение
Ref [2 ] является упрощенным представлением *(Ref + 2), а, следова-
тельно, представляет букву п. В итоге получим слово Jan = Jan.

Таким образом:

int Num; - декларация переменной типа (int)
int *Ptr; - (int*)

int **Ref; - (int**)

Объяление массивов.

Массив - переменная сложного типа, все элементы которой име-
ют тот же тип, что и сам массив. Можно объявлять одномерные и
многомерные массивы. Массивам, объявленным до определения фун-
кции, можно присваивать начальные значения. Запись, определяю-
щая начальные значения, называется инициатором и состоит из
списка литералов в { }. По умолчанию элементам массива присваи-
ваивается 0.

char Source [3][3] = {{'Е'>7а'}, {T,'z','a'},

{'JYaVn'}}

main ()
{

char Target [4 ];/*объявление второго массива */
int i;
for (i=0; i; i++)

Target [i ]=Source [2 ] [i ];
Target [3 К";
printf ("%s", Target);

}

Таким образом Target [0 ]... Target [3 ] = J a n код 0

В языке принято, что каждое выражение, представляющее мас-
сив, сразу же неявно преобразуется в выражение, представляющее
первый элемент массива, следовательно:

printf("%s", Target); эквивалентно printf("%s",
&Target [0 ]);

Такой способ трактовки имен массивов, как имен указателей,
приводит к тому, что выполнение операций над массивами может
быть в общем случае заменено выполнением операций над указыва-
ющими данными.

Кроме того, элементами массивов могут быть структуры, унии
и указывающие переменные:

char *Family [3 ] = {"Ewa'VTza'V'Jan"}

main ()
{

printf("%c&%c", **Family, *Family[2]);

}

Здесь элементами массива являются указатели данных типа
(char). При определении массива, его элементу Family [0 ] присвоено
указание на первый знак последовательности Ewa, аналогично,
Family [1 ] - Iza, a Family [2] - Jan. Т.к. выражение Family представ-
ляет собой указатель на Family [0 ], выражение *Family представляет
элемент Family [0 ], а, следовательно, **Family представляет собой
объект, указанный выражением Family [0 ], т.е. букву Е. Выполнение
программы приводит к печати Е & J.

Можно сказать, что если сам массив Family имеет тип (char * [3 ])
(3-х элементный массив указателей данных типа (char)), то выраже-
ние Family имеет тип (char *), выражение *Family - (char *), а выра-
жение **Family - (char).

В отличие от предыдущей), в данном примере используется пе-
ременная, указывающая на массив:
int Array [2 ] [2 ] = {{3,2}, {1,0}},

(*Ptr) [2] = Array+1;

main ()
{

printf ("% d%cT, **Ptr, Ptr [-1 ][0]);

}

Здесь Array - 2-х элементный массив, элементами которого яв-
ляются 2-х элементные массивы с элементами типа int. Массив Array
можно рассматривать как двумерный массив с элементами 3,2,1,0. В
то же время Ptr является простой переменной, указывающей на 2-х
элементный массив типа int. Ей приписано значение Аггау+1 и она
имеет тип (int* [2 ]). Такое присвоение правомочно, т.к. Ptr и Аггау+1
имеют одинаковый тип (int * [2 ]).

Объявление структур и уний

Структурами и униями являются сложные переменные, состоя-
щие из компонентов разного типа. Эти компоненты называются по-
лями. Полями могут быть не только простые переменные, но и
массивы, структуры и унии.

Поля структур размещаются в памяти в порядке их объявления,
а каждое поле унии размещается, начиная от одного и того же места.
Следовательно, если в программе объявлена структура Record:
struct {

int Fix;
char Chr;
int Arr[3];
char *Ptr [2 ]
} Record;

с полями Fix, Chr, Arr и Ptr, то она займет столько места, сколько
занимают все поля в сумме. Тогда как, если объявлена уния:
union {

int Fix;
char Chr;
int Arr [3 ];
char *Ptr[2];
} Record

то она займет столько места, сколько занимает самое длинное
поле. Учитывая, что в Hisoft переменная типа (char) занимает один
байт, а типа (int) и указывающие - по 2 байта, то структура займет
11 байт, а уния - 6 байт ОЗУ. Т.о. различия между униями и структу-
рами заключаются только в способе размещения в ОЗУ и в том, что
униям нельзя приписать начальное значение.

Объявление структуры состоит из ключевого слова struct, после
которого в { } стоит перечень полей структуры, а после него - декла-
ратор и знак ;. Декларатор может состоять из идентификатора струк-
туры, а может иметь и более сложный вид:

struct {
int Fix;

char Chr [2] [2];
} Str, Arr[3 ], *Ptr[4 ];

- декларация структуры Str с полями Fix и Chr, декларация
Зх-элементной таблицы структур Arr, а так же декларация 4х-эле-
ментной таблицы указателей структур таких как Str и Arr [i ].

Если между ключевым словом struct и первой скобкой стоит
идентификатор, то в дальнейших объявлениях он может быть исполь-
зован для идентифицирования структуры данного вида. В частности,
это означает, что такое объявление, как:
struct Tag {
int Fix;
char Chr;
} One, Two [2 ];

в которой Tag является идентификатором, в дальнейшем назы-
ваемым описателем структуры, может с равным успехом быть заме-
нено объявлением:
struct Tag {
int Fix;
char Chr;
} One

struct Tag Two [2];
или парой объявлений:
struct Tag {
int Fix;
char Chr;

};

struct Tag One, Two [2 ];

Использование описателя структуры целесообразно в тех случа-
ях, когда в процессе объявления поля структуры необходимо обраще-
ние к типу уже объявленной структуры:

struct List {
int Data;
struct List *Link;
} Str;

где полю Link могут быть присвоены указатели структур типа
(struct List).

Полям структур (но не уний) могут быть присвоены исходные
данные. Инициатор структуры состоит, в общем случае, из списка в
{}. Элементами списка могут быть константы, чаще всего литералы,
или подсписки; константы для инициализации скалярных полей, а
подсписки для инициализации полей, которые являются массивами
и структурами. Подсписок, используемый для инициализации поля,
имеет вид идентификатора объекта с типом этого поля. Если некото-
рый подсписок содержит полный комплект константных выражений,
инициирующих данное поле, то { } могут быть опущены. Если для
определенных полей не определены явно начальные данные, а объяв-
ление структуры находится вне определения функции, то таким по-
лям присваивается литерал ноль. Полям структуры, объявленной
внутри функции, не могут быть присвоены начальные значения,
struct {

char One, Two;
struct {
char Uno, Due;
} InStr;
char Arr[2],

*Ref*

} OutStr={l,2{3,4},{5}};

- полям структуры OutStr присвоены данные таким образом, что
полю Arr [0 ] будет присвоено 5, а полю Ref - 0.

Внимание: В Hisoft для упрощения компилятора принято, что
инициатор структуры не может содержать подсписок. Из этого сле-
дует, что в приведенной выше декларации инициатор должен быть
изменен на следующий: {1,2,3,4,5}. Другим отступлением является
то, что присвоение данных относится не к отдельным полям структу-
ры, а к области памяти, выделенной структуре. Учитывая, что эле-
ментом списка инициатора является выражение со значением 0-255,
то инициируется один или два байта области памяти, присвоенной
структуре:

struct {

int Uno, Due;
} Str= {1,1};

- присвоение полю Uno данного, величиной 257, а полю Due - 0,
вместо 1 для обоих полей.

Объявление функций.

Объявление функций состоит из заголовка и тела функции.
Заголовок содержит имя функции, в круглых скобках список пара-
метров функции и объявление параметров. Тело функции является
групповой командой, начинающейся {и кончающейся }. Если выпол-
нение функции заканчивается командой:
return ехр;

где ехр - выражение, то в месте вызова функции станут доступ-
ными данные ехр. Тип данных определяется на основе анализа заго-
ловка функции. В следующем примере на экране печатается Bielecki:

main ()
{

char *Surname О;

printf ("%s", Surname ("Jan Bielecki",4));
}

char *

Surname (String, Number)
char *String;
int Number;

{ return String + Number;}

Здесь определяются две функции: функция main () без Ларамет-
ров и функция Surname с двумя параметрами. Параметр String типа
(char*) связан с аргументом "Jan Bielecki", а параметр Number типа
(int) связан с аргументом 4. Результатом функции является данное,
выступающее в команде return. Это данное имеет тип (char *) в
соответствии с объявленным в заголовке функции типом. Так как
вызов функции происходит перед ее определением, в функции main
производится предварительная декларация. Эта декларация имеет
вид заголовка определения, из которого изъят список имен парамет-
ров и их объявление.

Если выполнение функции не заканчивается командой return,
содержащей выражение, то определение типа результата излишне. В
Си можно опускать объявление типа результата или параметров фун-
кции, если они имеют тип (int).
int_13=-13;
int

main ()
{

int Negate 0;

printf ("%d", Negate(__13));

}

int

Negate (Par)
int Par;
{ return -Par;}
- упрощается до:
int__13 = -13;

main ()
{

printf ("%dM, Negate,(_13));

}

Negate (Par)

{

return -Par;

}.

В следующей программе определяются функции First и Second
с предварительной декларацией этих функций.

char Name [2] = "bj";
extern char ""First 0; /* First */

main ()
{

printf("%c", *First());

}

char *

First 0
{

char *Second(); /* Second */
printf ("%c", *Second (Name));
return Name;

}

char *

Second (Initials)

char Initial [2 ];

{

return Initial + 1;

}

Результат работы программы - печать jb.

Описание типов.

Объявление (описание, преобразование) типа аналогично опре-
делению переменной за исключением ключевого слова typedef.

typedef char *PTR,

VEC [3 ];

- определяет тип PTR, идентифицирующий указатель данных
типа (char) и тип VEC, идентифицирующий 3-х элементный массив
данных типа (char). После такого определения типов можно напи-
сать:

PTR Arr [2], ChrPtr;

VEC *Ref;

В первой строке объявляется 2-х элементный массив перемен-
ных типа (PTR), а; следовательно, массив указателей данных типа
(char), и переменная ChrPtr, указывающая на такие данные. Во вто-
рой - указатель данных типа VEC, следовательно, указатель 3-х
элементных массивов типа (char).

Легко заметить, что такое определение равносильно:

char *Arr[2], ChrPtr;

char (*Ref) [3 ];

Пример:

typedef struct List {
int Data;

struct List *Linck;
} LIST;

- LIST является именем типа (struct List). Таким образом деклара-
ция List Str [3 ], *Ref; аналогична struct List

Str[3], *Ref

4. Построение выражений.

Основным элементом языка Си являются первичные выраже-
ния. Ими являются:

- идентификатор : Volume, Fun и др.;

-литерал: 13, "JB", 'W и др.;

- некоторое выражение в круглых скобках: (а + Ь);

- выражение, представляющее функцию, за которой следует
список аргументов в круглых скобках: Fun (a,b);

- первичное выражение, после которого стоит выражение в

квадратных скобках: Arr [3+i ];

- первичное 1-выражение (т.е. выражение, которое стоит слева
от знака или символа =), представляющее собой структуру,
сразу после которой стоит знак . и идентификатор поля
структуры: Str.Chr;

- первичное выражение, представляющее указатель структуры,
непосредственно после которого стоит символ и
идентификатор поля структуры: Ptr - Chr.

Дополнительные правила:

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

&Fix;

- в записи вида:

первичное-выражение (список-выражений)

Первичное выражение должно представлять функцию:
Fun(a,b); -в записи вида:

первичное-выражение [выражение ]

обязательно одно из выражений должно быть указателем на
элементы массива: 2 ["Jan" ].

Выражением является выражение с предшествующим операто-
ром конверсии (имя-типа). Если Ехр является выражением, пред-
ставляющим некоторое данное, то (имя-типа) Ехр является
выражением, представляющим данное типа (имя-типа). Именем ти-
па является запись, состоящая из описания типа и декларатора. Если,
к примеру, Ехр - выражение типа (int *), то (char *) Ехр представляет
данное типа (char *).

Внимание: В системе Hisoft запись (имя-типа), выступающая
в значении оператора конверсии, должна быть упреждена ключевым
словом cast: cast (char *) Ехр.

Выражением является имя типа в круглых скобках с предшест-
вующим оператором размера sizeof. Если Nam - имя типа, то
sizeof (Nam) является выражением, представляющим данные, вели-
чиной, равной числу байт, необходимых для хранения в ОЗУ внут-
реннего данного типа (Nam).

Внимание: В системе Hisoft имя типа должно быть ключевым
словом, означающим тип, к примеру, int или идентификатор типа.

Внимание: В системе Hisoft оператор sizeof не может отно-
ситься к выражениям.

Выражением является запись вида:

]-выражение оператор-присваивания выражение Оператор-
присваивания - =, *=, I =, %=, -=, <=, >=, &=, = и !=.

Выполнение операции а@=Ь, где @ - один из вышеуказанных
символов, равносильно выполнению операции а=а@Ь.

. Выражением является запись вида:

выражение? выражение-1: выражение-2

Если выражение имеет значение, отличное от 0, то результатом
3-х аргументной операции ?: является данное, представленное выра-
жением-1, в противном случае - выражением-2.

Пример:

char Arr [2] [2] = {'J', 'а', 'п', 'В'},
* Ptr;

main ()

{

typedef char ""ChrPtr;

Ptr = cast (Chrtr) (Arr+1)+1;

printf ("%c%c", ♦♦Arr, *Ptr);

}

Конверсия может быть явной и неявной. Явная конверсия явля-
ется выражением с предшествующим ключевым словом cast (только
в Hisoft). В примере применен оператор коверсии к выражению (Arr
+ 1). При этом выражение (Arr +1), представляющее данное типа
(char(*) [2]), преобразуется в выражение, представляющее данное
типа (char *), т.е. в выражение такого же типа, как и Ptr.

Если данные типа (char) присваиваются переменной (int) или
(unsigned), то при этом самый старший байт этой переменной запол-
нится нулями. И, наоборот, если данные типа (int) или (unsigned)
будут присвоены переменной тип (char), то переменной присвоится
младший байт данного. А если данное типа (int) присвоить перемен-
ной типа (unsigned) и наоборот, то можно потерять данные.

Неявная конверсия может выполняться и при выполнении
арифметических операций: - если некоторый аргумент имеет тип
(char), то он будет преобразован к типу (int); - если только один
аргумент 2х-аргументной операции имеет тип (unsigned), то второй
будет преобразован к этому же типу; - в других случаях оба аргумента
должны быть типа (int), таким же будет и результат.




СОДЕРЖАНИЕ:


  Оставте Ваш отзыв:

  НИК/ИМЯ
  ПОЧТА (шифруется)
  КОД



Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Инструментарий - Инструкция по пользованию пакетом программ "ZX-WINWORD", предназначенных для подготовки иллюстрированных текстов.
Версии - 2 версии игры: ALIEN STORM.
Интерфейс - Хаккеры ... Кто они? Зачем они нужны?
Вступление - содержание номера.
Мнение - пара слов от автора E-Mage Work Station.

В этот день...   21 ноября