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

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


    Прохождения игр    
Aliens Vs Predator |#6| We walk through the tunnels
Aliens Vs Predator |#5| Unexpected meeting
Aliens Vs Predator |#4| Boss fight with the Queen
Aliens Vs Predator |#3| Escaping from the captivity of the xenomorph

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


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

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

Предыдущая страница Следующая страница
1 ... 8 9 10 11 12 13 14  15 16 17 18 19 20 21 ... 87
кового.   То,  что  в  других  языках  (PL/1, Algol-68, Pascal) является частью языка
(встроено в язык)- в Си вынесено на уровень библиотек.  Например, в Си нет  оператора
вывода;  функция вывода printf - это библиотечная функция (хотя и общепринятая).  Та-
ким образом мощь языка Си состоит именно в том, что он позволяет  использовать  функ-
ции, написанные другими программистами и даже на других языках, т.е. является функци-
онально расширяемым.

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

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

     #include "файл.h"         /*  ./файл.h       */
     #include "../h/файл.h"    /*  ../h/файл.h    */
     #include "/usr/my/файл.h" /*  /usr/my/файл.h */

Непременно изучите содержимое стандартных include-файлов в своей системе!
     В качестве резюме - схема, поясняющая "превращения" Си-программы  из  текста  на
языке  программирования  в  выполняемый  код:  все  файлы .c могут использовать общие
include-файлы; их подстановку в текст, а также обработку #define  произведет  препро-
цессор cpp

    file1.c    file2.c    file3.c
      |          |          |       "препроцессор"
      | cpp      | cpp      | cpp
      |          |          |       "компиляция"
      | cc -c    | cc -c    | cc -c
      |          |          |
    file1.o    file2.o    file3.o
      |          |          |
      -----------*-----------
                 |       Неявно добавятся:
             ld  |<----- /lib/libc.a (библ. станд. функций)
                 |       /lib/crt0.o (стартер)
    "связывание" |
    "компоновка" |<----- Явно указанные библиотеки:
                 |       -lm       /lib/libm.a
                 V
               a.out

1.147.  Напоследок - простой, но жизненно важный совет.  Если  вы  пишете  программу,
которую вставите в систему для частого использования, поместите в исходный текст этой
программы идентификационную строку наподобие

    static char id[] = "This is /usr/abs/mybin/xprogram";

Тогда в случае аварии в файловой системе, если вдруг ваш файл "потеряется" (то есть у
него  пропадет  имя  -  например из-за порчи каталога), то он будет найден программой
проверки файловой системы - fsck - и помещен в каталог  /lost+found  под  специальным
кодовым  именем,  ничего  общего  не имеющим со старым.  Чтобы понять, что это был за
файл и во что его следует переименовать (чтобы восстановить правильное имя), мы  при-
меним команду

    strings имя_файла

Эта команда покажет все длинные строки из печатных символов,  содержащиеся  в  данном
файле,  в  частности  и  нашу строку id[].  Увидев ее, мы сразу поймем, что файл надо
переименовать так:

    mv имя_файла /usr/abs/mybin/xprogram

1.148.  Где размещать include-файлы и как программа узнает, где же они лежат?   Стан-
дартные  системные  include-файлы  размещены  в /usr/include и подкаталогах.  Если мы
пишем некую свою программу (проект) и используем директивы

    #include "имяФайла.h"

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

то обычно include-файлы имяФайла.h лежат в текущем каталоге (там же, где  и  файлы  с
программой  на  Си).   Однако  мы  можем помещать ВСЕ наши include-файлы в одно место
(скажем, известное группе программистов, работающих над одним  и  тем  же  проектом).
Хорошее место для всех ваших личных include-файлов - каталог (вами созданный)

    $HOME/include

где $HOME - ваш домашний каталог.  Хорошее место для общих include-файлов - каталог

    /usr/local/include

Как сказать компилятору, что #include "" файлы надо брать из определенного  места,  а
не из текущего каталога? Это делает ключ компилятора

    cc -Iимя_каталога ...

Например:

    /* Файл x.c */
    #include "x.h"

    int main(int ac, char *av[]){
            ....
            return 0;
    }

И файл x.h находится в каталоге /home/abs/include/x.h (/home/abs - мой домашний ката-
лог).  Запуск программы на компиляцию выглядит так:

    cc -I/home/abs/include -O x.c -o x
            или
    cc -I$HOME/include -O x.c -o x

Или, если моя программа x.c находится в /home/abs/progs

    cc -I../include -O x.c -o x

Ключ -O задает вызов компилятора с оптимизацией.
     Ключ -I оказывает влияние и на #include <<>> директивы тоже.  Для  ОС  Solaris  на
машинах Sun программы для оконной системы X Window System содержат строки вроде

    #include <<X11/Xlib.h>>
    #include <<X11/Xutil.h>>

На Sun эти файлы находятся не в /usr/include/X11, а в /usr/openwin/include/X11.  Поэ-
тому запуск на компиляцию оконных программ на Sun выглядит так:

    cc -O -I/usr/openwin/include xprogram.c \
          -o xprogram -L/usr/openwin/lib -lX11

где -lX11 задает подключение графической оконной библиотеки Xlib.
     Если include-файлы находятся во многих каталогах, то можно задать поиск  в  нес-
кольких каталогах, к примеру:

    cc -I/usr/openwin/include -I/usr/local/include -I$HOME/include ...

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

2.  Массивы, строки, указатели.
     Массив представляет собой агрегат из нескольких  переменных  одного  и  того  же
типа. Массив с именем a из LENGTH элементов типа TYPE объявляется так:

    TYPE a[LENGTH];

Это соответствует тому, что объявляются переменные типа TYPE со специальными  именами
a[0],  a[1],  ...,  a[LENGTH-1].   Каждый  элемент массива имеет свой номер - индекс.
Доступ к x-ому элементу массива осуществляется при помощи операции индексации:

    int x = ... ;      /* целочисленный индекс   */
    TYPE value = a[x]; /* чтение x-ого элемента  */
         a[x] = value; /* запись в x-тый элемент */

В качестве индекса может использоваться любое  выражение,  выдающее  значение  целого
типа:  char, short, int, long.  Индексы элементов массива в Си начинаются с 0 (а не с
1), и индекс последнего элемента массива из LENGTH элементов -  это  LENGTH-1  (а  не
LENGTH).  Поэтому цикл по всем элементам массива - это

    TYPE a[LENGTH]; int indx;
    for(indx=0; indx << LENGTH; indx++)
       ...a[indx]...;

indx < LENGTH  равнозначно  indx <= LENGTH-1.   Выход  за  границы  массива  (попытка
чтения/записи  несуществующего элемента) может привести к непредсказуемым результатам
и поведению программы.  Отметим, что это одна из самых распространенных ошибок.
     Статические массивы можно объявлять с  инициализацией,  перечисляя  значения  их
элементов  в  {}  через  запятую.   Если задано меньше элементов, чем длина массива -
остальные элементы считаются нулями:

    int a10[10] = { 1, 2, 3, 4 }; /* и 6 нулей */

Если при описании массива с инициализацией не указать его размер, он будет  подсчитан
компилятором:

    int a3[] = { 1, 2, 3 }; /* как бы a3[3] */

     В большинстве современных компьютеров (с фон-Неймановской  архитектурой)  память
представляет  собой массив байт.  Когда мы описываем некоторую переменную или массив,
в памяти выделяется непрерывная область для  хранения  этой  переменной.   Все  байты
памяти  компьютера  пронумерованы.   Номер байта, с которого начинается в памяти наша
переменная, называется адресом этой переменной (адрес может  иметь  и  более  сложную
структуру,  чем  просто  целое  число - например состоять из номера сегмента памяти и
номера байта в этом сегменте).  В Си адрес переменной можно получить с помощью опера-
ции  взятия  адреса &. Пусть у нас есть переменная var, тогда &var - ее адрес.  Адрес
нельзя присваивать целой переменной;  для  хранения  адресов  используются  указатели
(смотри ниже).
     Данное может занимать несколько подряд идущих байт.   Размер  в  байтах  участка
памяти,  требуемого для хранения значения типа TYPE, можно узнать при помощи операции
sizeof(TYPE), а размер  переменной  -  при  помощи  sizeof(var).  Всегда  выполняется
sizeof(char)==1.   В  некоторых машинах адреса переменных (а также агрегатов данных -
массивов и структур) кратны sizeof(int)  или  sizeof(double)  -  это  так  называемое
"выравнивание (alignment) данных на границу типа int".  Это позволяет делать доступ к
данным более быстрым (аппаратура работает эффективнее).
     Язык Си предоставляет нам средство для работы  с  адресами  данных  -  указатели
(pointer)[*].  Указатель физически - это адрес некоторой переменной ("указуемой"  пере-
менной).   Отличие  указателей от машинных адресов состоит в том, что указатель может
содержать адреса данных только определенного типа.  Указатель ptr, который может ука-
зывать на данные типа TYPE, описывается так:

    TYPE  var;     /* переменная       */
    TYPE *ptr;     /* объявление ук-ля */
          ptr = & var;

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

В данном случае мы занесли в указательную переменную ptr адрес переменной var.  Будем
говорить,  что  указатель ptr указывает на переменную var (или, что ptr установлен на
var).  Пусть TYPE равно int, и у нас есть массив и указатели:

    int  array[LENGTH], value;
    int *ptr, *ptr1;

Установим указатель на x-ый элемент массива

    ptr = & array[x];

Указателю можно присвоить значение другого указателя на такой же  тип.  В  результате
оба указателя будут указывать на одно и то же место в памяти: ptr1 = ptr;
     Мы можем изменять указуемую переменную при помощи операции *

    *ptr = 128;   /* занести 128 в указуемую перем. */
    value = *ptr; /* прочесть указуемую переменную  */

В данном случае мы заносим и затем читаем значение переменной  array[x],  на  которую
поставлен указатель, то есть

    *ptr  означает сейчас  array[x]

Таким образом, операция * (значение по адресу)  оказывается  обратной  к  операции  &
(взятие адреса):

    & (*ptr) == ptr    и    * (&value) == value

Операция * объясняет смысл описания TYPE *ptr; оно означает, что  значение  выражения
*ptr  будет иметь тип TYPE. Название же типа самого указателя - это (TYPE *). В част-
ности, TYPE может сам быть указательным типом - можно объявить  указатель  на  указа-
тель, вроде char **ptrptr;
     Имя массива - это константа, представляющая собой указатель на 0-ой элемент мас-
сива.   Этот указатель отличается от обычных тем, что его нельзя изменить (установить
на другую переменную), поскольку он сам хранится не в переменной, а  является  просто
некоторым постоянным адресом.

        массив           указатель
           ____________       _____
    array: | array[0] |   ptr:| * |
           | array[1] |         |
           | array[2] |<--------- сейчас равен &array[2]
           |  ...     |

Следствием такой интерпретации имен массивов является то, что для того  чтобы  поста-
вить указатель на начало массива, надо писать

    ptr = array;  или  ptr = &array[0];
            но не
    ptr = &array;

Операция & перед одиноким именем массива не нужна и недопустима!
     Такое родство указателей и массивов позволяет нам применять операцию *  к  имени
массива: value = *array; означает то же самое, что и value = array[0];
     Указатели - не целые числа!  Хотя физически это и номера байтов, адресная  ариф-
метика  отличается  от  обычной.  Так,  если  дан  указатель TYPE *ptr; и номер байта
(адрес), на который указывает ptr, равен byteaddr, то

    ptr = ptr + n; /* n - целое, может быть и < 0 */

заставит ptr указывать не на байт номер byteaddr + n, а на байт номер

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

    byteaddr + (n * sizeof(TYPE))

то есть прибавление единицы к указателю продвигает адрес не на 1 байт,  а  на  размер
указываемого  указателем  типа данных!  Пусть указатель ptr указывает на x-ый элемент
Предыдущая страница Следующая страница
1 ... 8 9 10 11 12 13 14  15 16 17 18 19 20 21 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама