Главная · Поиск книг · Поступления книг · 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 ... 27 28 29 30 31 32 33  34 35 36 37 38 39 40 ... 87
              \    =========men[1]==
               \-->|name  |    *---|-----> "Гриша"
                      ............

5.9.  Составьте программу "справочник по таблице  Менделеева",  которая  по  названию
химического элемента выдавала бы его характеристики. Таблицу инициализируйте массивом
структур.

5.10.  При записи данных в файл (да и вообще) используйте структуры вместо  массивов,
если  элементы массива имеют разное смысловое назначение.  Не воспринимайте структуру
просто как средство объединения данных разных типов, она может быть и средством объе-
динения  данных  одного  типа, если это добавляет осмысленности нашей программе.  Чем
плох фрагмент?

    int data[2];

    data[0] = my_key;
    data[1] = my_value;

    write(fd, (char *) data, 2 * sizeof(int));

Во-первых, тогда уж лучше указать размер всего массива сразу (хотя бы на тот  случай,
если мы изменим его размер на 3 и забудем поправить множитель с 2 на 3).

    write(fd, (char *) data, sizeof data);

Кстати, почему мы пишем data, а не &data? (ответ: потому что имя массива и  есть  его
адрес).   Во-вторых,  элементы массива имеют разный смысл, так не использовать ли тут
структуру?

    struct _data {
            int key;
            int value;
    } data;

    data.key   = my_key;
    data.value = my_value;

    write(fd, &data, sizeof data);

5.11.  Что напечатает следующая программа? Нарисуйте расположение указателей по окон-
чании данной программы.

    #include 
    struct lnk{
       char c;

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

       struct lnk *prev, *next;
    }  chain[20], *head = chain;

    add(c) char c;
    {
       head->c = c;
       head->next = head+1;
       head->next->prev = head;
       head++;
    }
    main(){
       char *s = "012345";
       while( *s ) add( *s++ );
       head->c = '-';
       head->next = (struct lnk *)NULL;
       chain->prev = chain->next;
       while( head->prev ){
            putchar( head->prev->c );
            head = head->prev;
            if( head->next )
                head->next->prev = head->next->next;
       }
    }

5.12.  Напишите программу, составлящую двунаправленный список букв, вводимых с клави-
атуры.  Конец  ввода  -  буква '\n'.  После третьей буквы вставьте букву '+'. Удалите
пятую  букву.   Распечатайте   список   в   обратном   порядке.   Оформите   операции
вставки/удаления как функции.  Элемент списка должен иметь вид:

      struct elem{
             char  letter;       /* буква         */
             char  *word;        /* слово         */
             struct elem *prev;  /* ссылка назад  */
             struct elem *next;  /* ссылка вперед */
      };
      struct elem *head, /* первый элемент списка */
                  *tail, /* последний элемент     */
                  *ptr,  /* рабочая переменная    */
                  *prev; /* предыдущий элемент при просмотре */
      int c, cmp;
              ...
      while((c = getchar()) != '\n' )
            Insert(c, tail);
      for(ptr=head; ptr != NULL; ptr=ptr->next)
            printf("буква %c\n", ptr->letter);

Память лучше отводить не из массива, а функцией calloc(), которая аналогична  функции
malloc(),  но дополнительно расписывает выделенную память байтом '\0' (0, NULL).  Вот
функции вставки и удаления:

    extern char *calloc();
    /* создать новое звено списка для буквы c */
    struct elem *NewElem(c) char c; {
       struct elem *p = (struct elem *)
                        calloc(1, sizeof(struct elem));
       /* calloc автоматически обнуляет все поля,
        * в том числе prev и next
        */
       p->letter = c; return p;
    }

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

    /* вставка после ptr (обычно - после tail) */
    Insert(c, ptr) char c; struct elem *ptr;
    {  struct elem *newelem = NewElem(c), *right;
       if(head == NULL){  /* список был пуст */
          head=tail=newelem; return;  }
       right = ptr->next; ptr->next = newelem;
       newelem->prev = ptr; newelem->next = right;
       if( right ) right->prev = newelem;
       else        tail        = newelem;
    }

    /* удалить ptr из списка */
    Delete( ptr ) struct elem *ptr; {
       struct elem *left=ptr->prev, *right=ptr->next;
       if( right ) right->prev = left;
       if( left  ) left->next  = right;
       if( tail == ptr ) tail  = left;
       if( head == ptr ) head  = right;
       free((char *) ptr);
    }

Напишите аналогичную программу для списка слов.

    struct elem *NewElem(char *s) {
       struct elem *p = (struct elem *)
         calloc(1, sizeof(struct elem));
       p->word = strdup(s);
       return p;
    }
    void DeleteElem(struct elem *ptr){
            free(ptr->word);
            free(ptr);
    }

Усложнение: вставляйте слова в список в алфавитном порядке.   Используйте  для  этого
функцию strcmp(), просматривайте список так:

    struct elem *newelem;

    if (head == NULL){  /* список пуст */
        head = tail = NewElem(новое_слово);
        return;
    }
    /* поиск места в списке */
    for(cmp= -1, ptr=head, prev=NULL;
        ptr;
        prev=ptr, ptr=ptr->next
    )
    if((cmp = strcmp(новое_слово, ptr->word)) <= 0 )
              break;

Если цикл окончился с cmp==0, то такое слово уже есть в  списке.   Если  cmp < 0,  то
такого  слова  не  было  и  ptr  указывает элемент, перед которым надо вставить слово
новое_слово, а prev - после которого (prev==NULL означает, что надо вставить в начало
списка);  т.е. слово вставляется между prev и ptr.  Если cmp > 0, то слово надо доба-
вить в конец списка (при этом ptr==NULL).

    head ==> "a" ==> "b" ==> "d" ==> NULL
              |               |
             prev    "c"     ptr

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

    if(cmp == 0) return; /* слово уже есть */
    newelem = NewElem( новое_слово );
    if(prev == NULL){       /* в начало */
       newelem->next = head;
       newelem->prev = NULL;
       head->prev    = newelem;
       head          = newelem;
    } else if(ptr == NULL){ /* в конец */
       newelem->next = NULL;
       newelem->prev = tail;
       tail->next    = newelem;
       tail          = newelem;
    } else {                /* между prev и ptr */
       newelem->next = ptr;
       newelem->prev = prev;
       prev->next    = newelem;
       ptr ->prev    = newelem;
    }

5.13.  Напишите функции для работы с комплексными числами

       struct complex {
              double re, im;
       };

Например, сложение выглядит так:

       struct complex add( c1, c2 )
              struct complex c1, c2;
       {
              struct complex sum;
              sum.re = c1.re + c2.re;
              sum.im = c1.im + c2.im;
              return sum;
       }

       struct complex a = { 12.0, 14.0 },
                      b = { 13.0, 2.0  };
       main(){
              struct complex c;
              c = add( a, b );
              printf( "(%g,%g)\n", c.re, c.im );
       }

5.14.  Массивы в Си нельзя  присваивать  целиком,  зато  структуры  -  можно.  Иногда
используют такой трюк: структуру из единственного поля-массива

    typedef struct {
            int ai[5];
    } intarray5;
    intarray5 a, b = { 1, 2, 3, 4, 5 };

и теперь законно

    a = b;

Зато доступ к ячейкам массива выглядит теперь менее изящно:

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

    a.ai[2] = 14;
    for(i=0; i < 5; i++) printf( "%d\n", a.ai[i] );

Также невозможно передать копию массива в качестве  фактического  параметра  функции.
Даже если мы напишем:

    typedef int ARR16[16];
    ARR16 d;
    void f(ARR16 a){
      printf( "%d %d\n", a[3], a[15]);
      a[3] = 2345;
    }
    void main(void){
      d[3] = 9; d[15] = 98;
      f(d);
      printf("Now it is %d\n", d[3]);
    }

то последний printf напечатает "Now it is 2345", поскольку в f передается адрес  мас-
сива,  но  не его копия; поэтому оператор a[3]=2345 изменяет исходный массив.  Обойти
это можно, использовав тот же трюк, поскольку при передаче структуры в качестве пара-
метра  передается  уже не ее адрес, а копия всей структуры (как это и принято в Си во
всех случаях, кроме массивов).

5.15.  Напоследок упомянем про битовые поля - элементы структуры,  занимающие  только
часть  машинного  слова  - только несколько битов в нем. Размер поля в битах задается
конструкцией :число_битов. Битовые поля используются для более  компактного  хранения
информации в структурах (для экономии места).

    struct XYZ {
       /* битовые поля должны быть unsigned */
       unsigned x:2;   /* 0 .. 2**2 - 1 */
       unsigned y:5;   /* 0 .. 2**5 - 1 */
       unsigned z:1;   /* YES=1 NO=0    */
    } xyz;
    main(){
      printf("%u\n", sizeof(xyz)); /* == sizeof(int) */
      xyz.z = 1; xyz.y = 21; xyz.x = 3;
      printf("%u %u %u\n", xyz.x, ++xyz.y, xyz.z);

      /* Значение битового поля берется по модулю
       * максимально допустимого числа 2**число_битов - 1
       */
    xyz.y = 32 /* максимум */ + 7; xyz.x = 16+2; xyz.z = 11;
    printf("%u %u %u\n", xyz.x, xyz.y, xyz.z); /* 2 7 1 */
    }

Поле ширины 1 часто используется в качестве битового флага: вместо

    #define FLAG1   01
    #define FLAG2   02
    #define FLAG3   04
    int x;  /* слово для нескольких флагов */
    x |= FLAG1; x &= ~FLAG2; if(x & FLAG3) ...;

используется

    struct flags {
           unsigned flag1:1, flag2:1, flag3:1;
    } x;
    x.flag1 = 1; x.flag2 = 0; if( x.flag3 ) ...;

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

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

5.16.  Пример на использование структур с полем переменного размера.  Часть  перемен-
ной  длины  может быть лишь одна и обязана быть последним полем структуры.  Внимание:
это программистский трюк, использовать осторожно!

    #include 
    #define SZ 5
    extern char *malloc();
    #define VARTYPE char

    struct obj {
            struct header {   /* постоянная часть */
                    int cls;
                    int size; /* размер переменной части */
            } hdr;

            VARTYPE body [1];    /* часть переменного размера:
                                 в описании ровно ОДИН элемент массива */
    } *items [SZ];            /* указатели на структуры */

    #define OFFSET(field, ptr)        ((char *) &ptr->field - (char *)ptr)
    int body_offset;

    /* создание новой структуры */
    struct obj *newObj( int cl, char *s )
    {
        char *ptr; struct obj *op;
        int n = strlen(s);  /* длина переменной части (штук VARTYPE) */
        int newsize = sizeof(struct header) + n * sizeof(VARTYPE);

        printf("[n=%d newsize=%d]\n", n, newsize);

        /* newsize = (sizeof(struct obj) - sizeof(op->body)) + n * sizeof(op->body);

           При использовании этого размера не учитывается, что struct(obj)
           выровнена на границу sizeof(int).
           Но в частности следует учитывать и то, на границу чего выровнено
           начало поля op->body. То есть самым правильным будет

           newsize = body_offset + n * sizeof(op->body);

        */

        /* отвести массив байт без внутренней структуры */
        ptr = (char *) malloc(newsize);

        /* наложить поверх него структуру */
        op = (struct obj *) ptr;

        op->hdr.cls  = cl;
        op->hdr.size = n;
Предыдущая страница Следующая страница
1 ... 27 28 29 30 31 32 33  34 35 36 37 38 39 40 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама