Главная · Поиск книг · Поступления книг · 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 ... 13 14 15 16 17 18 19  20 21 22 23 24 25 26 ... 87
ется  признаком  конца '\0'), в Си предусмотрены также функции для работы с массивами
байт без ограничителя. Для таких функций необходимо явно указывать длину обрабатывае-
мого  массива.   Напишите функции: пересылки массива длиной n байт memcpy(dst,src,n);
заполнения массива символом c   memset(s,c,n);  поиска  вхождения  символа  в  массив
memchr(s,c,n); сравнения двух массивов               memcmp(s1,s2,n); Ответ:

    #define REG register
    char *memset(s, c, n) REG char *s, c;
    {    REG char *p = s;
         while( --n >= 0 ) *p++ = c;
         return s;
    }
    char *memcpy(dst, src, n)
          REG char *dst, *src;
          REG int n;
    {     REG char *d = dst;

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

          while( n-- > 0 ) *d++ = *src++;
          return dst;
    }
    char *memchr(s, c, n) REG char *s, c;
    {
          while(n-- && *s++ != c);
          return( n < 0 ? NULL : s-1 );
    }
    int memcmp(s1, s2, n)
          REG char *s1, *s2; REG n;
    {
          while(n-- > 0 && *s1 == *s2)
            s1++, s2++;
          return( n < 0 ? 0 : *s1 - *s2 );
    }

Есть такие стандартные функции.

2.57.  Почему лучше пользоваться стандартными функциями работы со строками и  памятью
(strcpy, strlen, strchr, memcpy, ...)?
     Ответ: потому, что они обычно реализованы поставщиками  системы  ЭФФЕКТИВНО,  то
есть  написаны не на Си, а на ассемблере с использованием специализированных машинных
команд и регистров. Это делает их более быстрыми.  Написанный Вами эквивалент  на  Си
может  использоваться для повышения мобильности программы, либо для внесения поправок
в стандартные функции.

2.58.  Рассмотрим программу, копирующую строку саму в себя:

    #include 
    #include 

    char string[] = "abcdefghijklmn";
    void main(void){
            memcpy(string+2, string, 5);
            printf("%s\n", string);
            exit(0);

Она печатает abababahijklmn.  Мы могли бы ожидать, что кусок длины 5 символов "abcde"
будет  скопирован как есть: ab[abcde]hijklmn, а получили ab[ababa]hijklmn - цикличес-
кое повторение первых двух символов строки... В чем  дело?  Дело  в  том,  что  когда
области  источника  (src)  и  получателя  (dst) перекрываются, то в некий момент *src
берется из УЖЕ перезаписанной ранее области, то  есть  испорченной!   Вот  программа,
иллюстрирующая эту проблему:

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

    #include 
    #include 
    #include 

    char string[] = "abcdefghijklmn";
    char *src = &string[0];
    char *dst = &string[2];
    int n     = 5;

    void show(int niter, char *msg){
            register length, i;

            printf("#%02d %s\n", niter, msg);
            length = src-string;
            putchar('\t');
            for(i=0; i < length+3; i++) putchar(' ');
            putchar('S');  putchar('\n');

            printf("\t...%s...\n", string);

            length = dst-string;
            putchar('\t');
            for(i=0; i < length+3; i++) putchar(' ');
            putchar('D');  putchar('\n');
    }

    void main(void){
            int iter = 0;

            while(n-- > 0){
                    show(iter,   "перед");
                      *dst++ = toupper(*src++);
                    show(iter++, "после");
            }
            exit(0);
    }

Она печатает:

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

    #00 перед
               S
            ...abcdefghijklmn...
                 D
    #00 после
                S
            ...abAdefghijklmn...
                  D
    #01 перед
                S
            ...abAdefghijklmn...
                  D
    #01 после
                 S
            ...abABefghijklmn...
                   D
    #02 перед
                 S
            ...abABefghijklmn...
                   D
    #02 после
                  S
            ...abABAfghijklmn...
                    D
    #03 перед
                  S
            ...abABAfghijklmn...
                    D
    #03 после
                   S
            ...abABABghijklmn...
                     D
    #04 перед
                   S
            ...abABABghijklmn...
                     D
    #04 после
                    S
            ...abABABAhijklmn...
                      D

Отрезки НЕ перекрываются, если один из них лежит либо  целиком  левее,  либо  целиком
правее другого (n - длина обоих отрезков).

    dst        src                  src        dst
    ########   @@@@@@@@             @@@@@@@@   ########

       dst+n <= src         или          src+n <= dst
       dst <= src-n         или          dst >= src+n

Отрезки перекрываются в случае

    ! (dst <= src - n || dst >= src + n) =
      (dst >  src - n && dst <  src + n)

При этом опасен только случай dst > src.  Таким образом опасная ситуация  описывается
условием

    src < dst && dst < src + n

(если dst==src, то вообще ничего не надо делать).  Решением является копирование  "от

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

хвоста к голове":

    void bcopy(register char *src, register char *dst,
               register int n){

            if(dst >= src){
                    dst += n-1;
                    src += n-1;
                    while(--n >= 0)
                            *dst-- = *src--;
            }else{
                    while(n-- > 0)
                            *dst++ = *src++;
            }
    }

Или, ограничиваясь только опасным случаем:

    void bcopy(register char *src, register char *dst,
               register int n){

            if(dst==src || n <= 0) return;
            if(src < dst && dst < src + n) {
                    dst += n-1;
                    src += n-1;
                    while(--n >= 0)
                            *dst-- = *src--;
            }else   memcpy(dst, src, n);
    }

Программа

    #include 
    #include 
    #include 

    char string[] = "abcdefghijklmn";
    char *src = &string[0];
    char *dst = &string[2];
    int n     = 5;

    void show(int niter, char *msg){
            register length, i;

            printf("#%02d %s\n", niter, msg);
            length = src-string;
            putchar('\t');
            for(i=0; i < length+3; i++) putchar(' ');
            putchar('S');  putchar('\n');

            printf("\t...%s...\n", string);

            length = dst-string;
            putchar('\t');
            for(i=0; i < length+3; i++) putchar(' ');
            putchar('D');  putchar('\n');
    }

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

    void main(void){
            int iter = 0;

            if(dst==src || n <= 0){
                    printf("Ничего не надо делать\n");
                    return;
            }

            if(src < dst && dst < src + n) {
                    dst += n-1;
                    src += n-1;
                    while(--n >= 0){
                            show(iter,   "перед");
                              *dst-- = toupper(*src--);
                            show(iter++, "после");
                    }
            }else
                    while(n-- > 0){
                            show(iter,   "перед");
                              *dst++ = toupper(*src++);
                            show(iter++, "после");
                    }
            exit(0);
    }

Печатает

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

    #00 перед
                   S
            ...abcdefghijklmn...
                     D
    #00 после
                  S
            ...abcdefEhijklmn...
                    D
    #01 перед
                  S
            ...abcdefEhijklmn...
                    D
    #01 после
                 S
            ...abcdeDEhijklmn...
                   D
    #02 перед
                 S
            ...abcdeDEhijklmn...
                   D
    #02 после
                S
            ...abcdCDEhijklmn...
                  D
    #03 перед
                S
            ...abcdCDEhijklmn...
                  D
    #03 после
               S
            ...abcBCDEhijklmn...
                 D
    #04 перед
               S
            ...abcBCDEhijklmn...
                 D
    #04 после
              S
            ...abABCDEhijklmn...
                D

Теперь bcopy() - удобная функция для копирования и сдвига массивов, в частности  мас-
сивов указателей.  Пусть у нас есть массив строк (выделенных malloc-ом):

    char *lines[NLINES];

Тогда циклическая перестановка строк выглядит так:

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

    void scrollUp(){
            char *save = lines[0];
            bcopy((char *) lines+1, /* from */
                  (char *) lines,   /* to */
                  sizeof(char *) * (NLINES-1));
            lines[NLINES-1] = save;
    }
    void scrollDown(){
            char *save = lines[NLINES-1];
            bcopy((char *) &lines[0], /* from */
                  (char *) &lines[1], /* to */
                  sizeof(char *) * (NLINES-1));
            lines[0] = save;
    }

Возможно, что написание по аналогии функции для копирования массивов  элементов  типа
(void *) - обобщенных указателей - может оказаться еще понятнее и эффективнее.  Такая
функция - memmove - стандартно существует в UNIX SVR4.  Заметьте, что  порядок  аргу-
ментов в ней обратный по отношению к bcopy.  Следует отметить, что в SVR4 все функции
mem... имеют указатели типа (void *) и счетчик типа size_t - тип для количества  байт
(вместо unsigned long); в частности длина файла имеет именно этот тип (смотри систем-
ные вызовы lseek и stat).

    #include 

    void memmove(void *Dst, const void *Src,
                 register size_t n){

                register caddr_t src = (caddr_t) Src,
                                 dst = (caddr_t) Dst;

                if(dst==src || n <= 0) return;
                if(src < dst && dst < src + n) {
                        dst += n-1;
                        src += n-1;
                        while(--n >= 0)
                                *dst-- = *src--;
                }else   memcpy(dst, src, n);
    }

caddr_t - это тип для указателей на БАЙТ, фактически  это  (unsigned char *).   Зачем
вообще понадобилось использовать caddr_t?  Затем, что для

    void *pointer;
    int n;

значение

    pointer + n

не определено и невычислимо, ибо sizeof(void) не имеет смысла - это не  0,  а  просто
ошибка, диагностируемая компилятором!

2.59.  Еще об опечатках: вот что бывает, когда вместо знака `='  печатается  `-'  (на
клавиатуре они находятся рядом...).

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

    #include 
    #include 
    char *strdup(const char *s){
            extern void *malloc();
            return strcpy((char *)malloc(strlen(s)+1), s);
    }
    char *ptr;
    void main(int ac, char *av[]){
            ptr - strdup("hello"); /* подразумевалось ptr = ... */
            *ptr = 'H';
            printf("%s\n", ptr);
            free(ptr);
            exit(0);
    }

Дело в том, что запись (а часто и чтение) по *pointer, где pointer==NULL, приводит  к
аварийному прекращению программы. В нашей программе ptr осталось равным NULL - указа-
телем в никуда.  В операционной системе UNIX на машинах с аппаратной защитой  памяти,
страница  памяти,  содержащая  адрес NULL (0) бывает закрыта на запись, поэтому любое
Предыдущая страница Следующая страница
1 ... 13 14 15 16 17 18 19  20 21 22 23 24 25 26 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама