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

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


    Прохождения игр    
Demon's Souls |#13| Storm King
Demon's Souls |#12| Old Monk & Old Hero
Demon's Souls |#11| Мaneater part 2
Demon's Souls |#10| Мaneater (part 1)

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


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

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

Предыдущая страница Следующая страница
1 ... 5 6 7 8 9 10 11  12 13 14 15 16 17 18 ... 87

Формат здесь - условное выражение. Если x!=0, то будет напечатано значение x по  фор-
мату  %d.  Если же x==0, то будет напечатана строка, не содержащая ни одного %-та.  В
результате аргумент x в списке аргументов будет просто проигнорирован.  Однако,  нап-
ример

    int x = ... ;
    printf( x > 30000 ? "%f\n" : "%d\n", x);

(чтобы большие x печатались в виде 31000.000000)  незаконно,  поскольку  целое  число
нельзя  печатать по формату %f ни в каких случаях.  Единственным способом сделать это
является явное приведение x к типу double:

    printf("%f\n", (double) x);

Будет ли законен оператор?

    printf( x > 30000 ? "%f\n" : "%d\n",
            x > 30000 ? (double) x : x );

Ответ: нет. Условное выражение для аргумента будет иметь "старший" тип  -  double.  А
значение  типа  double  нельзя  печатать по формату %d.  Мы должны использовать здесь
оператор if:

    if( x > 30000 ) printf("%f\n", (double)x);
    else            printf("%d\n", x);

1.132.  Напишите функцию, печатающую размер файла в удобном виде:  если  файл  меньше
одного  килобайта  -  печатать  его размер в байтах, если же больше - в килобайтах (и
мегабайтах).

    #define KBYTE    1024L  /* килобайт */
    #define THOUSAND 1024L  /* кб. в мегабайте */

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

    void tellsize(unsigned long sz){
      if(sz < KBYTE) printf("%lu байт", sz);
      else{
        unsigned long Kb  = sz/KBYTE;
        unsigned long Mb  = Kb/THOUSAND;
        unsigned long Dec = ((sz % KBYTE) * 10) / KBYTE;
          if( Mb ){
              Kb %= THOUSAND;
    printf( Dec ? "%lu.%03lu.%01lu Мб." : "%lu.%lu Мб.",
                    Mb,   Kb,   Dec );
            } else
    printf( Dec ? "%lu.%01lu Кб.":"%lu Кб.", Kb, Dec);
      }
      putchar('\n');
    }

1.133.  Для печати строк используйте

            printf("%s", string);   /* A */
    но не   printf(string);         /* B */

Если мы используем вариант B, а в строке встретится символ '%'

    char string[] = "abc%defg";

то %d будет воспринято как формат для вывода целого числа.  Во-первых, сама строка %d
не  будет  напечатана;  во-вторых - что же будет печататься по этому формату, когда у
нас есть лишь единственный аргумент - string?! Напечатается какой-то мусор!

1.134.  Почему оператор

    char s[20];
    scanf("%s", s); printf("%s\n", s);

в ответ на ввод строки

      Пушкин  А.С.

печатает только "Пушкин"?
     Ответ: потому, что концом текста при вводе по формату %s считается либо \n, либо
пробел,  либо  табуляция,  а  не только \n; то есть формат %s читает слово из текста.
Чтение всех символов до конца строки, (включая пробелы) должно выглядеть так:

    scanf("%[^\n]\n", s);
      %[^\n] - читать любые символы, кроме \n (до \n)
      \n     - пропустить \n на конце строки
      %[abcdef] - читать слово,
                  состоящее из перечисленных букв.
      %[^abcde] - читать слово из любых букв,
        кроме перечисленных (прерваться по букве из списка).

Пусть теперь строки входной информации имеют формат:

       Фрейд Зигмунд 1856 1939

Пусть мы хотим считывать в строку s фамилию, в целое y - год рождения, а прочие  поля
- игнорировать. Как это сделать?  Нам поможет формат "подавление присваивания" %*:

     scanf("%s%*s%d%*[^\n]\n",
            s,   &y );

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

%* пропускает поле по формату, указанному после *, не занося его значение ни в  какую
переменную, а просто "забывая" его.  Так формат

            "%*[^\n]\n"

игнорирует "хвост" строки, включая символ перевода строки.
     Символы " ", "\t", "\n" в формате вызывают  пропуск  всех  пробелов,  табуляций,
переводов строк во входном потоке, что можно описать как

    int c;
    while((c = getc(stdin))== ' ' || c == '\t' || c == '\n' );

либо как формат

      %*[ \t\n]

     Перед числовыми форматами (%d, %o, %u, %ld, %x, %e, %f),  а  также  %s,  пропуск
пробелов делается автоматически.  Поэтому

            scanf("%d%d",  &x, &y);
                    и
            scanf("%d %d", &x, &y);

равноправны (пробел перед вторым %d просто не нужен).  Неявный  пропуск  пробелов  не
делается перед %c и %[... , поэтому в ответ на ввод строки "12 5 x" пример

    main(){ int n, m; char c;
       scanf("%d%d%c", &n, &m, &c);
       printf("n=%d m=%d c='%c'\n", n, m, c);
    }

напечатает "n=12 m=5 c=' '", то есть в c будет прочитан пробел (предшествовавший  x),
а не x.
     Автоматический пропуск пробелов перед %s не позволяет считывать  по  %s  строки,
лидирующие пробелы которых должны сохраняться.  Чтобы лидирующие пробелы также считы-
вались, следует использовать формат

     scanf("%[^\n]%*1[\n]", s);

в котором модификатор длины 1 заставляет игнорировать только один символ  \n,   а  не
ВСЕ  пробелы  и переводы строк, как "\n".  К сожалению (как показал эксперимент) этот
формат не в состоянии прочесть пустую строку (состоящую только из \n). Поэтому  можно
сделать глобальный вывод: строки надо считывать при помощи функций gets() и fgets()!

1.135.  Еще пара слов про scanf: scanf возвращает число успешно прочитанных им данных
(обработанных  %-ов)  или  EOF в конце файла. Неудача может наступить, если данное во
входном потоке не соответствует формату, например строка

            12 quack
      для
            int d1; double f; scanf("%d%lf", &d1, &f);

В этом случае scanf прочтет 12 по формату %d в переменную d1, но слово quack не отве-
чает формату %lf, поэтому scanf прервет свою работу и выдаст значение 1 (успешно про-
чел один формат).  Строка quack останется невостребованной - ее прочитают последующие
вызовы функций чтения; а сейчас f останется неизмененной.

1.136.  Си имеет квалификатор const, указывающий, что значение является  не  перемен-
ной,  а  константой, и попытка изменить величину по этому имени является ошибкой.  Во
многих случаях const может заменить #define, при этом еще явно указан тип  константы,
что полезно для проверок компилятором.

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

    const int x = 22;
    x = 33; /* ошибка: константу нельзя менять */

Использование const с указателем:
Указуемый объект - константа

         const char *pc = "abc";
         pc[1]  = 'x';    /* ошибка */
         pc     = "123";  /* OK */

Сам указатель - константа

         char *const cp = "abc";
         cp[1]  = 'x';    /* OK */
         cp     = "123";  /* ошибка */

Указуемый объект и сам указатель - константы

         const char *const cpc = "abc";
         cpc[1] = 'x';    /* ошибка */
         cpc    = "123";  /* ошибка */

Указатель на константу необходимо объявлять как const TYPE*

               int a = 1;
         const int b = 2;

         const int *pca  = &a;   /* OK, просто рассматриваем a как константу */
         const int *pcb  = &b;   /* OK */

               int *pb   = &b;   /* ошибка, так как тогда возможно было бы написать */
                   *pb   = 3;    /* изменить константу b */

1.137.  Стандартная функция быстрой сортировки  qsort  (алгоритм  quick  sort)  имеет
такой формат: чтобы отсортировать массив элементов типа TYPE

    TYPE arr[N];
          надо вызывать
    qsort(arr,/* Что сортировать? Не с начала: arr+m    */
          N,  /* Сколько первых элементов массива?      */
              /* можно сортировать только часть: n < N  */
          sizeof(TYPE),/* Или sizeof arr[0]             */
                       /* размер одного элемента массива*/
          cmp);

где

    int cmp(TYPE *a1, TYPE *a2);

функция сравнения элементов *a1 и *a2.  Ее аргументы - АДРЕСА двух каких-то элементов
сортируемого  массива.   Функцию  cmp мы должны написать сами - это функция, задающая
упорядочение элементов массива.  Для сортировки по возрастанию функция  cmp()  должна
возвращать целое

       < 0, если  *a1 должно идти раньше *a2    <
       = 0, если  *a1 совпадает с        *a2   ==
       > 0, если  *a1 должно идти после  *a2    >

Для массива строк элементы  массива имеют тип (char  *),  поэтому  аргументы  функции
имеют тип (char **).  Требуемому условию удовлетворяет такая функция:

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

            char *arr[N]; ...
            cmps(s1, s2) char **s1, **s2;
            { return strcmp(*s1, *s2); }

(Про strcmp смотри раздел "Массивы и строки").  Заметим,  что  в  некоторых  системах
программирования (например в TurboC++ [*]) вы должны использовать функцию  сравнения  с
прототипом

    int cmp (const void *a1, const void *a2);

и внутри нее явно делать приведение типа:

    cmps (const void *s1, const void *s2)
    { return strcmp(*(char **)s1, *(char **)s2); }

или можно поступить следующим образом:

    int cmps(char **s1, char **s2){
        return strcmp(*s1, *s2);
    }
    typedef int (*CMPS)(const void *, const void *);
    qsort((void *) array, ..., ..., (CMPS) cmps);

Наконец, возможно и просто объявить

    int cmps(const void *A, const void *B){
        return strcmp(A, B);
    }

Для массива целых годится такая функция сравнения:

            int arr[N]; ...
            cmpi(i1, i2) int *i1, *i2;
            { return *i1 - *i2; }

Для массива структур, которые мы сортируем по целому полю key, годится

            struct XXX{ int key; ... } arr[N];
            cmpXXX(st1, st2) struct XXX *st1, *st2;
            { return( st1->key  -  st2->key ); }

Пусть у нас есть массив long. Можно ли использовать

            long arr[N]; ...
            cmpl(L1, L2) long *L1, *L2;
            { return *L1 - *L2; }

Ответ: оказывается, что нет. Функция cmpl должна возвращать целое,  а  разность  двух
long-ов  имеет  тип  long.   Поэтому компилятор приводит эту разность к типу int (как
правило обрубанием старших битов).  При этом (если long-числа были велики)  результат
может изменить знак! Например:

    main(){
      int n; long a = 1L; long b = 777777777L;
      n = a - b;  /* должно бы быть отрицательным... */
      printf( "%ld %ld %d\n", a, b, n );
    }
____________________
   [*] TurboC - компилятор Си в MS DOS, разработанный фирмой Borland International.

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

печатает 1 777777777 3472.  Функция сравнения должна выглядеть так:

            cmpl(L1, L2) long *L1, *L2; {
                    if( *L1 == *L2 ) return   0;
                    if( *L1 <  *L2 ) return (-1);
                                     return   1;
            }

или

            cmpl(L1, L2) long *L1, *L2; {
                return( *L1 == *L2 ?  0 :
                        *L1 <  *L2 ? -1 : 1 );
            }

поскольку важна не величина возвращенного значения, а только ее знак.
     Учтите, что для использования функции сравнения вы должны либо определить  функ-
цию сравнения до ее использования в qsort():

            int cmp(...){ ... } /* реализация */
                    ...
            qsort(..... , cmp);

либо предварительно объявить имя функции сравнения, чтобы компилятор понимал, что это
именно функция:

            int cmp();
            qsort(..... , cmp);
                    ...
            int cmp(...){ ... } /* реализация */

1.138.  Пусть у нас есть две программы, пользующиеся одной и той же структурой данных
W:

     a.c                          b.c
    --------------------------   ------------------------------
    #include            #include 
    struct W{ int x,y; }a;       struct W{ int x,y; }b;
    main(){  int fd;             main(){  int fd;
      a.x = 12; a.y = 77;          fd = open("f", O_RDONLY);
      fd = creat("f", 0644);       read(fd, &b, sizeof b);
Предыдущая страница Следующая страница
1 ... 5 6 7 8 9 10 11  12 13 14 15 16 17 18 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама