Главная · Поиск книг · Поступления книг · 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 ... 7 8 9 10 11 12 13  14 15 16 17 18 19 20 ... 87
"побочных эффектов", не предусмотренных  программистом  при  ее  вызове  (программист
обычно  смотрит только на заголовок функции, и не выискивает "тайные" связи функции с
программой через глобальные переменные, если только  это  специально  не  оговорено).
Вот пример не реентерабельной функции:

    FILE *fp; ...  /* глобальный аргумент */
    char delayedInput ()
    {
         static char prevchar;  /* память */
         char c;
         c = prevchar;
         prevchar = getc (fp);
         return c;
    }

А вот ее реентерабельный эквивалент:

    char delayedInput (char *prevchar, FILE *fp)
    {
         char c;
         c = *prevchar;
         *prevchar = getc (fp);
         return c;
    }
    /* вызов: */
    FILE *fp1, *fp2; char prev1, prev2, c1, c2;
     ... x1 = delayedInput (&prev1, fp1);
         x2 = delayedInput (&prev2, fp2); ...

Как видим, все "запоминающие" переменные (т.е. prevchar) вынесены из самой функции  и
подаются в нее в виде аргумента.
     Реентерабельные функции независимы от остальной части программы (их можно скопи-
ровать  в другой программный проект без изменений), более понятны (поскольку все зат-
рагиваемые ими внешние переменные перечислены как аргументы,  не  надо  выискивать  в
теле  функции глобальных переменных, передающих значение в/из функции, т.е. эта функ-
ция не имеет побочных влияний), более надежны (хотя бы потому, что компилятор в  сос-
тоянии  проверить  прототип  такой  функции и предупредить вас, если вы забыли задать
какой-то аргумент; если же аргументы передаются  через  глобальные  переменные  -  вы
можете  забыть проинициализировать какую-то из них).  Старайтесь делать функции реен-
терабельными!

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

     Вот еще один пример на эту тему. Не-реентерабельный вариант:

    int x, y, result;
    int f (){
            static int z = 4;
            y = x + z; z = y - 1;
            return x/2;
    }
    Вызов:     x=13; result = f(); printf("%d\n", y);

А вот реентерабельный эквивалент:

    int y, result, zmem = 4;
    int f (/*IN*/ int x, /*OUT*/ int *ay, /*INOUT*/ int *az){
            *az = (*ay = x + *az) - 1;
            return x/2;
    }
    Вызов:    result = f(13, &y, &zmem); printf("%d\n", y);

1.145.  То, что формат заголовка функции должен быть известен компилятору до  момента
ее использования, побуждает нас помещать определение функции до точки ее вызова. Так,
если main вызывает f, а f вызывает g, то в файле функции расположатся в порядке

    g()   {              }
    f()   { ... g(); ... }
    main(){ ... f(); ... }

Программа обычно разрабатывается "сверху-вниз" - от main к деталям.  Си же  вынуждает
нас  размещать  функции  в программе в обратном порядке, и в итоге программа читается
снизу-вверх - от деталей к main, и читать ее следует от конца файла к началу!
Так мы вынуждены писать, чтобы удовлетворить Си-компилятор:

    #include 

    unsigned long g(unsigned char *s){
            const int BITS = (sizeof(long) * 8);
            unsigned long sum = 0;

            for(;*s; s++){
                    sum ^= *s;
                    /* cyclic rotate left */
                    sum = (sum<<1)|(sum>>(BITS-1));
            }
            return sum;
    }
    void f(char *s){
            printf("%s %lu\n", s, g((unsigned char *)s));
    }
    int main(int ac, char *av[]){
            int i;

            for(i=1; i < ac; i++)
                    f(av[i]);
            return 0;
    }

А вот как мы разрабатываем программу:

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

    #include 

    int main(int ac, char *av[]){
            int i;

            for(i=1; i < ac; i++)
                    f(av[i]);
            return 0;
    }
    void f(char *s){
            printf("%s %lu\n", s, g((unsigned char *)s));
    }
    unsigned long g(unsigned char *s){
            const int BITS = (sizeof(long) * 8);
            unsigned long sum = 0;

            for(;*s; s++){
                    sum ^= *s;
                    /* cyclic rotate left */
                    sum = (sum<<1)|(sum>>(BITS-1));
            }
            return sum;
    }

и вот какую ругань производит Си-компилятор в ответ на эту программу:

    "0000.c", line 10: identifier redeclared: f
            current : function(pointer to char) returning void
            previous: function() returning int : "0000.c", line 7
    "0000.c", line 13: identifier redeclared: g
            current : function(pointer to uchar) returning ulong
            previous: function() returning int : "0000.c", line 11

Решением проблемы является - задать прототипы (объявления заголовков) всех функций  в
начале файла (или даже вынести их в header-файл).

    #include 

    int main(int ac, char *av[]);
    void f(char *s);
    unsigned long g(unsigned char *s);
            ...

Тогда функции будет можно располагать в тексте в любом порядке.

1.146.  Рассмотрим процесс сборки программы из нескольких файлов на языке Си.   Пусть
мы  имеем  файлы file1.c, file2.c, file3.c (один из них должен содержать среди других
функций функцию main).  Ключ компилятора -o заставляет  создавать  выполняемую  прог-
рамму  с  именем, указанным после этого ключа. Если этот ключ не задан - будет создан
выполняемый файл a.out

    cc file1.c file2.c file3.c -o file

Мы получили выполняемую программу file.  Это эквивалентно 4-м командам:

    cc -c file1.c           получится     file1.o
    cc -c file2.c                         file2.o
    cc -c file3.c                         file3.o
    cc file1.o file2.o file3.o -o file

Ключ -c заставляет  компилятор  превратить  файл  на  языке  Си  в  "объектный"  файл

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

(содержащий  машинные  команды;  не будем вдаваться в подробности). Четвертая команда
"склеивает" объектные файлы в единое целое - выполняемую программу[*].  При этом,  если
какие-то  функции, используемые в нашей программе, не были определены (т.е. спрограм-
мированы нами) ни в одном из наших файлов - будет просмотрена библиотека  стандартных
функций.  Если  же  каких-то  функций  не  окажется и там - будет выдано сообщение об
ошибке.
     Если у нас уже есть какие-то готовые объектные  файлы,  мы  можем  транслировать
только новые Си-файлы:

    cc -c file4.c
    cc file1.o file2.o file3.o file4.o -o file
       или (что то же самое,
       но cc сам разберется, что надо делать)
    cc file1.o file2.o file3.o file4.c -o file

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

    ar r file.a file1.o file2.o file3.o

Будет создана библиотека file.a, содержащая перечисленные .o файлы (имена библиотек в
UNIX  имеют  суффикс  .a  - от слова archive, архив).  После этого можно использовать
библиотеку:

    cc file4.o file5.o file.a -o file

Механизм таков: если в файлах file4.o и file5.o не определена какая-то функция (функ-
ции), то просматривается библиотека, и в список файлов для "склейки" добавляется файл
из библиотеки, содержащий определение этой функции (из библиотеки он не  удаляется!).
Тонкость: из библиотеки берутся не ВСЕ файлы, а лишь те, которые содержат определения
недостающих функций[**].  Если, в свою очередь, файлы, извлекаемые из библиотеки,  будут
содержать  неопределенные функции - библиотека (библиотеки) будут просмотрены еще раз
и.т.д. (на самом деле достаточно максимум двух проходов, так как при первом просмотре
библиотеки  можно  составить  ее  каталог:  где какие функции в ней содержатся и кого
вызывают).  Можно указывать и несколько библиотек:

    cc file6.c file7.o  \
       file.a mylib.a /lib/libLIBR1.a -o file

Таким образом, в команде cc можно смешивать имена файлов: исходных текстов на Си  .c,
объектных файлов .o и файлов-библиотек .a.
     Просмотр  библиотек,  находящихся  в  стандартных  местах  (каталогах   /lib   и
/usr/lib),  можно  включить  и  еще  одним способом: указав ключ -l.  Если библиотека
называется

    /lib/libLIBR1.a   или     /usr/lib/libLIBR2.a

то подключение делается ключами

    -lLIBR1           и       -lLIBR2
____________________
   [*] На самом деле, для "склейки" объектных файлов в выполняемую  программу,  команда
/bin/cc  вызывает программу /bin/ld - link editor, linker, редактор связей, компонов-
щик.
   [**] Поэтому библиотека может быть очень большой, а к  нашей  программе  "приклеится"
лишь небольшое число файлов из нее. В связи с этим стремятся делать файлы, помещаемые
в библиотеку, как можно меньше: 1 функция;  либо  "пачка"  функций,  вызывающих  друг
друга.

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

соответственно.

    cc file1.c file2.c file3.o mylib.a -lLIBR1 -o file

Список библиотек и ключей -l должен идти после имен всех исходных .c и  объектных  .o
файлов.
     Библиотека стандартных функций языка  Си  /lib/libc.a  (ключ  -lc)  подключается
автоматически  ("подключить" библиотеку - значит вынудить компилятор просматривать ее
при сборке, если какие-то функции, использованные вами, не были вами определены),  то
есть  просматривается  всегда  (именно  эта  библиотека  содержит коды, например, для
printf, strcat, read).
     Многие прикладные пакеты функций поставляются именно в  виде  библиотек.   Такие
библиотеки состоят из ряда .o файлов, содержащих объектные коды для различных функций
(т.е. функции в скомпилированном виде).  Исходные тексты от большинства библиотек  не
поставляются  (так как являются коммерческой тайной). Тем не менее, вы можете исполь-
зовать эти функции, так как вам предоставляются разработчиком:
-    описание (документация).
-    include-файлы,  содержащие  форматы  данных  используемые  функциями  библиотеки
     (именно  эти  файлы включались #include в исходные тексты библ. функций.  Теперь
     уже вы должны включать их в свою программу).
Таким образом вы знаете, как надо вызывать библиотечные  функции  и  какие  структуры
данных вы должны использовать в своей программе для обращения к ним (хотя и не имеете
текстов самих библиотечных функций, т.е. не знаете, как они  устроены.  Например,  вы
часто  используете  printf(),  но  задумываетесь  ли вы о ее внутреннем устройстве?).
Некоторые библиотечные функции могут быть вообще написаны не на Си, а  на  ассемблере
или другом языке программирования[*][*].  Еще раз обращаю ваше внимание,  что  библиотека
содержит  не исходные тексты функций, а скомпилированные коды (и include-файлы содер-
жат (как правило) не тексты функций, а только описание форматов данных)!   Библиотека
может также содержать статические данные, вроде массивов строк-сообщений об ошибках.
     Посмотреть список файлов, содержащихся в библиотеке, можно командой

    ar tv имяФайлаБиблиотеки

а список имен функций - командой

    nm имяФайлаБиблиотеки

Извлечь файл (файлы) из архива (скопировать его в текущий каталог), либо удалить  его
из библиотеки можно командами

    ar x имяФайлаБиблиотеки имяФайла1 ...
    ar d имяФайлаБиблиотеки имяФайла1 ...

где ... означает список имен файлов.
     "Лицом" библиотек служат прилагаемые к ним  include-файлы.   Системные  include-
файлы, содержащие общие форматы данных для стандартных библиотечных функций, хранятся
в каталоге /usr/include  и подключаются так:

    для /usr/include/файл.h     надо  #include <файл.h>
    для /usr/include/sys/файл.h       #include 

____________________
   [*][*] Обратите внимание, что библиотечные функции не являются частью ЯЗЫКА Си как та-
Предыдущая страница Следующая страница
1 ... 7 8 9 10 11 12 13  14 15 16 17 18 19 20 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама