Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Demon's Souls |#13| Storm King
Demon's Souls |#11| Мaneater part 2
Demon's Souls |#10| Мaneater (part 1)
Demon's Souls |#9| Heart of surprises

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Богатырев А. Весь текст 1009.15 Kb

Хрестоматия по программированию на Си в Unix

Предыдущая страница Следующая страница
1 ... 59 60 61 62 63 64 65  66 67 68 69 70 71 72 ... 87

    mov пересылка (:=)    add сложение
    sub вычитание         cmp сравнение и выработка признака
    jmp переход           jeq переход, если ==
    jlt переход, если <   jle переход, если <=
    neg изменение знака   not инвертирование признака

7.71.  Напишите программу, преобразующую определения функций Си в  "старом"  стиле  в
"новый" стиль стандарта ANSI ("прототипы" функций).

            f(x, y, s, v)
                  int x;
                  char *s;
                  struct elem *v;
            { ... }

преобразуется в

            int f(int x, int y, char *s, struct elem *v)
            { ... }

(обратите внимание, что переменная y и сама функция f описаны по умолчанию как  int).

А. Богатырев, 1992-95                  - 344 -                              Си в UNIX

Еще пример:

       char *ff()    { ... }
            заменяется на
       char *ff(void){ ... }

В данной задаче вам возможно придется использовать программу lex.
     В списке аргументов прототипа должны быть явно указаны типы  всех  аргументов  -
описатель int нельзя опускать.  Так

    q(x, s) char *s; { ... }  // не прототип, допустимо.
                              // x - int по умолчанию.
    q(x,     char *s);        // недопустимо.
    q(int x, char *s);        // верно.

Собственно под "прототипом" понимают предварительное описание функции в новом стиле -
где вместо тела {...} сразу после заголовка стоит точка с запятой.

    long f(long x, long y);                /* прототип */
            ...
    long f(long x, long y){ return x+y; }  /* реализация */

В прототипе имена аргументов можно опускать:

    long f(long, long);                /* прототип */
    char *strchr(char *, char);

Это предварительное описание помещают  где-нибудь  в  начале  программы,  до  первого
вызова функции.  В современном Си прототипы заменяют описания вида

    extern long f();

о которых мы говорили раньше.   Прототипы  предоставляют  программисту  механизм  для
автоматического контроля формата вызова функции.  Так, если функция имеет прототип

    double f( double );

и вызывается как

    double x = f( 12 );

то компилятор автоматически превратит это в

    double x = f( (double) 12 );

(поскольку существует приведение типа от int к double); если же написано

    f( "привет" );

то компилятор сообщит об ошибке (так как нет преобразования типа (char *) в double).
Прототип принуждает компилятор проверять:
a)   соответствие ТИПОВ фактических параметров (при вызове) типам формальных парамет-
     ров (в прототипе);
b)   соответствие КОЛИЧЕСТВА фактических и формальных параметров;
c)   тип возвращаемого функцией значения.

Прототипы обычно помещают в include-файлы. Так в ANSI стандарте Си предусмотрен файл,
подключаемый

            #include 

А. Богатырев, 1992-95                  - 345 -                              Си в UNIX

в котором определены прототипы функций из стандартной библиотеки языка  Си.  Черезвы-
чайно  полезно  писать  эту директиву include, чтобы компилятор проверял, верно ли вы
вызываете стандартные функции.
     Заметим, что если вы определили прототипы каких-то функций, но в своей программе
используете  не все из этих функций, то функции, соответствующие "лишним" прототипам,
НЕ будут добавляться к вашей программе из библиотеки. Т.е.  прототипы - это  указание
компилятору;  ни  в какие машинные команды они не транслируются. То же самое касается
описаний внешних переменных и функций в виде

            extern int x;
            extern char *func();

Если вы не используете переменную или функцию с таким именем, то эти строки не  имеют
никакого эффекта (как бы вообще отсутствуют).

7.72.  Обратная задача: напишите преобразователь из нового стиля в старый.

            int f( int x, char *y ){ ... }

переводить в

            int f( x, y ) int x; char *y; { ... }

7.73.  Довольно легко использовать прототипы таким образом, что они  потеряют  всякий
смысл.  Для этого надо написать программу, состоящую из нескольких файлов, и в каждом
файле использовать свои прототипы для одной и той же функции.  Так бывает,  когда  вы
поменяли  функцию  и  прототип в одном файле, быть может во втором, но забыли сделать
это в остальных.

    --------
    файл a.c
    --------
    void g(void);
    void h(void);

    int x = 0, y = 13;

    void f(int arg){
            printf("f(%d)\n", arg);
            x = arg;
            x++;
    }

    int main(int ac, char *av[]){
            h();
            f(1);
            g();
            printf("x=%d y=%d\n", x, y);
            return 0;
    }

А. Богатырев, 1992-95                  - 346 -                              Си в UNIX

    --------
    файл b.c
    --------
    extern int x, y;

    int f(int);

    void g(){
            y = f(5);
    }

    --------
    файл c.c
    --------
    void f();

    void h(){
            f();
    }

Выдача программы:

    abs@wizard$ cc a.c b.c c.c -o aaa
    a.c:
    b.c:
    c.c:
    abs@wizard$ aaa
    f(-277792360)
    f(1)
    f(5)
    x=6 y=5
    abs@wizard$

Обратите внимание, что во всех трех файлах f() имеет разные прототипы!  Поэтому прог-
рамма печатает нечто, что довольно-таки бессмысленно!
     Решение таково: стараться вынести прототипы  в  include-файл,  чтобы  все  файлы
программы  включали одни и те же прототипы.  Стараться, чтобы этот include-файл вклю-
чался также в файл с самим определением функции.  В  таком  случае  изменение  только
заголовка  функции  или только прототипа вызовет ругань компилятора о несоответствии.
Вот как должен выглядеть наш проект:

    -------------
    файл header.h
    -------------
    extern int x, y;
    void f(int arg);
    int main(int ac, char *av[]);
    void g(void);
    void h(void);

А. Богатырев, 1992-95                  - 347 -                              Си в UNIX

    --------
    файл a.c
    --------
    #include "header.h"

    int x = 0, y = 13;

    void f(int arg){
            printf("f(%d)\n", arg);
            x = arg;
            x++;
    }

    int main(int ac, char *av[]){
            h();
            f(1);
            g();
            printf("x=%d y=%d\n", x, y);
            return 0;
    }

    --------
    файл b.c
    --------
    #include "header.h"

    void g(){
            y = f(5);
    }

    --------
    файл c.c
    --------
    #include "header.h"

    void h(){
            f();
    }

Попытка компиляции:

    abs@wizard$ cc a.c b.c c.c -o aaa
    a.c:
    b.c:
    "b.c", line 4: operand cannot have void type: op "="
    "b.c", line 4: assignment type mismatch:
            int "=" void
    cc: acomp failed for b.c
    c.c:
    "c.c", line 4: prototype mismatch: 0 args passed, 1 expected
    cc: acomp failed for c.c

А. Богатырев, 1992-95                  - 348 -                              Си в UNIX

8.  Экранные библиотеки и работа с видеопамятью.
     Терминал в UNIX с точки зрения программ - это файл.  Он представляет  собой  два
устройства:  при записи write() в этот файл осуществляется вывод на экран; при чтении
read()-ом из этого файла - читается информация с клавиатуры.
     Современные терминалы в определенном смысле являются устройствами  прямого  дос-
тупа:
-    информация может быть выведена в любое место экрана, а не только последовательно
     строка за строкой.
-    некоторые терминалы позволяют прочесть содержимое произвольной области экрана  в
     вашу программу.
Традиционные терминалы являются самостоятельными устройствами, общающимися с  компью-
тером  через линию связи. Протокол[*] общения образует систему команд терминала и может
быть различен для терминалов разных моделей. Поэтому библиотека работы с традиционным
терминалом должна решать следующие проблемы:
-    настройка на систему команд данного устройства, чтобы одна  и  та  же  программа
     работала на разных типах терминалов.
-    эмуляция недостающих в системе команд; максимальное использование  предоставлен-
     ных терминалом возможностей.
-    мимнимизация передачи данных через линию связи (для ускорения работы).
-    было бы полезно, чтобы библиотека предоставляла пользователю некоторые  логичес-
     кие  абстракции,  вроде  ОКОН  -  прямоугольных областей на экране, ведущих себя
     подобно маленьким терминалам.

     В UNIX эти задачи решает стандартная библиотека curses (а только первую задачу -
более  простая библиотека termcap). Для настройки на систему команд конкретного дисп-
лея  эти  библиотеки  считывают  описание  системы   команд,   хранящееся   в   файле
/etc/termcap.  Кроме них бывают и другие экранные библиотеки, а также существуют иные
способы работы с экраном (через видеопамять, см. ниже).
     В задачах данного раздела вам придется пользоваться библиотекой curses. При ком-
пиляции  программ эта библиотека подключается при помощи указания ключа -lcurses, как
в следующем примере:

    cc progr.c -Ox -o progr -lcurses -lm

Здесь  подключаются  две  библиотеки:  /usr/lib/libcurses.a  (работа  с  экраном)   и
/usr/lib/libm.a  (математические  функции,  вроде  sin, fabs).  Ключи для подключения
библиотек должны быть записаны в команде САМЫМИ ПОСЛЕДНИМИ. Заметим, что  стандартная
библиотека  языка  Си (содержащая системные вызовы, библиотеку stdio (функции printf,
scanf, fread, fseek, ...), разные часто употребляемые функции (strlen, strcat, sleep,
malloc,  rand,  ...))  /lib/libc.a  подключается  автоматически и не требует указания
ключа -lc.
     В начале своей программы вы должны написать директиву

    #include 

подключающую файл /usr/include/curses.h, в котором описаны форматы данных, используе-
мых  библиотекой curses, некоторые предопределенные константы и.т.п. (это надо, чтобы
ваша программа пользовалась именно этими  стандартными  соглашениями).  Посмотрите  в
этот файл!
     Когда вы пользуетесь curses-ом, вы НЕ должны пользоваться функциями  стандартной
библиотеки stdio для непосредственного вывода на экран; так вы не должны пользоваться
____________________
   [*] Под протоколом в программировании подразумевают ряд соглашений двух сторон (сер-
вера  и  клиентов;  двух машин в сети (кстати, термин для обозначения машины в сети -
"host" или "site")) о формате (правилах оформления) и смысле  данных  в  передаваемых
друг  другу сообщениях.  Аналогия из жизни - человеческие речь и язык.  Речь всех лю-
дей состоит из одних и тех же звуков и может быть записана одними и теми  же  буквами
(а  данные  -  байтами).   Но  если  два человека говорят на разных языках - т.е. по-
разному конструируют фразы и интерпретируют звуки - они не поймут друг друга!

А. Богатырев, 1992-95                  - 349 -                              Си в UNIX

функциями printf, putchar.  Это происходит потому, что curses хранит  в  памяти  про-
цесса копию содержимого экрана, и если вы выводите что-либо на экран терминала обходя
функции библиотеки curses, то реальное содержимое экрана и  позиция  курсора  на  нем
перестают соответствовать хранимым в памяти, и библиотека curses начнет выводить неп-
равильное изображение.

       ПРОГРАММА
        |  |
        | CURSES---копия экрана
        | printw,addch,move
        |  |
        V  V
     библиотека STDIO --printf,putchar----> экран

Таким образом, curses является дополнительным "слоем" между вашей программой и  стан-
дартным выводом и игнорировать этот слой не следует.
     Напомним, что изображение, создаваемое при  помощи  библиотеки  curses,  сначала
формируется  в  памяти программы без выполнения каких-либо операций с экраном дисплея
(т.е. все функции wmove, waddch, waddstr,  wprintw  изменяют  только  ОБРАЗЫ  окон  в
памяти,  а  на экране ничего не происходит!).  И лишь только ПОСЛЕ того, как вы вызо-
Предыдущая страница Следующая страница
1 ... 59 60 61 62 63 64 65  66 67 68 69 70 71 72 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама