Главная · Поиск книг · Поступления книг · 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 2 3 4  5 6 7 8 9 10 11 12 13 14 ... 87
только из самого внутреннего цикла (на один уровень).

1.53.  К какому if-у относится else?

    if(...) ... if(...) ... else ...

Ответ: ко второму (к ближайшему предшествующему,  для  которого  нет  другого  else).
Вообще же лучше явно расставлять скобки (для ясности):

    if(...){ ... if(...) ...   else ... }
    if(...){ ... if(...) ... } else ...

1.54.  Макроопределение, чье тело представляет собой последовательность операторов  в
{...}  скобках (блок), может вызвать проблемы при использовании его в условном опера-
торе if с else-частью:

    #define MACRO   { x=1; y=2; }

    if(z)   MACRO;
    else    .......;

Мы получим после макрорасширения

    if(z)   { x=1; y=2; } /* конец if-а */ ;
    else    .......;      /* else ни к чему не относится */

то есть синтаксически ошибочный фрагмент, так как должно быть либо

    if(...)  один_оператор;
    else     .....
             либо
    if(...){ последовательность; ...; операторов; }
    else     .....

где точка-с-запятой после } не нужна.  С этим явлением борются, оформляя блок {...} в
виде do{...}while(0)

    #define MACRO do{ x=1; y=2; }while(0)

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

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

    if(z)   do{ x=1; y=2; }while(0);
    else    .......;

1.55.  В чем ошибка (для знающих язык "Паскаль")?

    int  x = 12;
         if( x < 20 and x > 10 ) printf( "O'K\n");
    else if( x > 100 or x < 0  ) printf( "Bad x\n");
    else                         printf( "x=%d\n", x);

Напишите

    #define and &&
    #define or  ||

1.56.  Почему программа зацикливается?  Мы хотим подсчитать число пробелов и  табуля-
ций в начале строки:

    int i = 0;
    char *s = "   3 spaces";
    while(*s == ' ' || *s++ == '\t')
            printf( "Пробел %d\n", ++i);

Ответ: логические операции || и && выполняются слева  направо;  как  только  какое-то
условие  в  ||  оказывается  истинным  (а в && ложным) - дальнейшие условия просто не
вычисляются. В нашем случае условие *s==' ' сразу же верно, и операция s++ из второго
условия не выполняется! Мы должны были написать хотя бы так:

    while(*s == ' ' || *s == '\t'){
            printf( "Пробел %d\n", ++i); s++;
    }

С другой стороны, это свойство || и && черезвычайно полезно, например:

    if( x != 0.0 && y/x < 1.0 ) ... ;

Если бы мы не вставили проверку на 0, мы могли бы получить деление на 0.  В данном же
случае при x==0 деление просто не будет вычисляться.  Вот еще пример:

    int a[5], i;
    for(i=0; i < 5 && a[i] != 0; ++i) ...;

Если i выйдет за границу массива, то сравнение a[i] с нулем уже не будет вычисляться,
т.е. попытки прочесть элемент не входящий в массив не произойдет.
     Это свойство && позволяет писать довольно неочевидные конструкции, вроде

    if((cond) && f());
        что оказывается эквивалентным
    if( cond ) f();

Вообще же

    if(C1 && C2 && C3) DO;
            эквивалентно
    if(C1) if(C2) if(C3) DO;

и для "или"

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

    if(C1 || C2 || C3) DO;
            эквивалентно
         if(C1) goto ok;
    else if(C2) goto ok;
    else if(C3){ ok: DO; }

Вот еще пример, пользующийся этим свойством ||

    #include 
    main(argc, argv) int argc; char *argv[];
    {  FILE *fp;
       if(argc < 2 || (fp=fopen(argv[1], "r")) == NULL){
            fprintf(stderr, "Плохое имя файла\n");
            exit(1); /* завершить программу */
       }
       ...
    }

Если argc==1, то argv[1] не определено, однако в этом случае попытки открыть  файл  с
именем argv[1] просто не будет предпринято!
     Ниже приведен еще один содержательный пример, представляющий собой одну из  воз-
можных схем написания "двуязычных" программ, т.е. выдающих сообщения на одном из двух
языков по вашему желанию. Проверяется переменная окружения MSG (или LANG):

                                    ЯЗЫК:
    1)  "MSG=engl"                английский
    2)   MSG нет в окружении      английский
    3)  "MSG=rus"                 русский

Про окружение и функцию getenv() смотри в главе "Взаимодействие с UNIX", про strchr()
- в главе "Массивы и строки".

    #include 
    int _ediag = 0; /* язык диагностик: 1-русский */
    extern char *getenv(), *strchr();
    #define ediag(e,r) (_ediag?(r):(e))
    main(){  char *s;
    _ediag = ((s=getenv("MSG"))   != NULL   &&
               strchr("rRрР", *s) != NULL);
    printf(ediag("%d:english\n", "%d:русский\n"), _ediag);
    }

Если переменная MSG не определена, то s==NULL и функция strchr(s,...)  не  вызывается
(ее  первый фргумент не должен быть NULL-ом).  Здесь ее можно было бы упрощенно заме-
нить на *s=='r'; тогда если s равно NULL, то обращение *s было бы незаконно  (обраще-
ние  по  указателю NULL дает непредсказуемые результаты и, скорее всего, вызовет крах
программы).

1.57.  Иногда логическое условие можно сделать более понятным, используя правила  де-
Моргана:

    a && b     =     ! ( !a || !b )
    a || b     =     ! ( !a && !b )

а также учитывая, что

    ! !a       =     a
    ! (a == b) =    (a != b)

Например:

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

    if( c != 'a' && c != 'b' && c != 'c' )...;
            превращается в
    if( !(c == 'a' || c == 'b' || c == 'c')) ...;

1.58.  Пример, в котором используются побочные эффекты вычисления выражений.   Обычно
значение  выражения присваивается некоторой переменной, но это не необходимо. Поэтому
можно использовать свойства вычисления && и || в выражениях (хотя это не  есть  самый
понятный способ написания программ, скорее некоторый род извращения). Ограничение тут
таково: все части выражения должны возвращать значения.

    #include 
    extern int errno;       /* код системной ошибки */
    FILE *fp;

    int openFile(){
            errno = 0;
            fp = fopen("/etc/inittab", "r");
            printf("fp=%x\n", fp);
            return(fp == NULL ? 0 : 1);
    }
    int closeFile(){
            printf("closeFile\n");
            if(fp) fclose(fp);
            return 0;
    }

    int die(int code){
            printf("exit(%d)\n", code);
            exit(code);
            return 0;
    }

    void main(){
            char buf[2048];

            if( !openFile()) die(errno); closeFile();
            openFile()    || die(errno); closeFile();
              /* если файл открылся, то die() не вычисляется */
            openFile() ? 0 : die(errno); closeFile();

            if(openFile()) closeFile();
            openFile()  && closeFile();
              /* вычислить closeFile() только если openFile() удалось */
            openFile()  && (printf("%s", fgets(buf, sizeof buf, fp)), closeFile());
    }

В последней строке использован оператор "запятая": (a,b,c) возвращает значение  выра-
жения c.

1.59.  Напишите функцию, вычисляющую сумму массива заданных чисел.

1.60.  Напишите функцию, вычисляющую среднее значение массива заданных чисел.

1.61.  Что будет напечатано в результате работы следующего цикла?

    for ( i = 36; i > 0; i /= 2 )
          printf ( "%d%s", i,
                           i==1 ? ".\n":", ");

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

Ответ: 36, 18, 9, 4, 2, 1.

1.62.  Найдите ошибки в следующей программе:

    main {
         int i, j, k(10);

         for ( i = 0, i <= 10, i++   ){
               k[i] = 2 * i + 3;
               for ( j = 0, j <= i, j++ )
                     printf ("%i\n", k[j]);
         }
    }

Обратите внимание на формат %i, существует ли такой формат?  Есть ли это тот  формат,
по которому следует печатать значения типа int?

1.63.  Напишите программу, которая распечатывает  элементы  массива.  Напишите  прог-
рамму, которая распечатывает элементы массива по 5 чисел в строке.

1.64.  Составьте программу считывания строк символов из стандартного ввода  и  печати
номера введенной строки, адреса строки в памяти ЭВМ, значения строки, длины строки.

1.65.  Стилистическое замечание: в операторе return возвращаемое выражение не  обяза-
тельно должно быть в ()-скобках.  Дело в том, что return - не функция, а оператор.

    return  выражение;
    return (выражение);

Однако если вы вызываете функцию (например, exit) - то аргументы должны быть в  круг-
лых скобках: exit(1); но не exit 1;

1.66.  Избегайте ситуации, когда функция в разных  ветвях  вычисления  то  возвращает
некоторое значение, то не возвращает ничего:

    int func (int x) {
        if( x > 10  ) return x*2;
        if( x == 10 ) return (10);
        /* а здесь - неявный return; без значения */
    }

при x < 10 функция вернет непредсказуемое значение!   Многие  компиляторы  распознают
такие ситуации и выдают предупреждение.

1.67.  Напишите программу, запрашивающую ваше имя и "приветствующую"  вас.   Напишите
функцию чтения строки. Используйте getchar() и printf().
Ответ:

    #include   /* standard input/output */
    main(){
       char buffer[81]; int i;
       printf( "Введите ваше имя:" );
       while((i = getstr( buffer, sizeof buffer )) != EOF){
              printf( "Здравствуй, %s\n", buffer );
              printf( "Введите ваше имя:" );
       }
    }

    getstr( s, maxlen )
     char *s;    /* куда поместить строку */
     int maxlen; /* длина буфера:

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

                    макс. длина строки = maxlen-1 */
    {  int c;    /* не char! (почему ?) */
       register int i = 0;

       maxlen--; /* резервируем байт под конечный '\0' */
       while(i < maxlen && (c = getchar()) != '\n'
                        &&  c != EOF )
              s[i++] = c;
       /* обратите внимание, что сам символ '\n'
        * в строку не попадет */
       s[i] = '\0';  /* признак конца строки */
       return (i == 0 && c == EOF) ? EOF : i;
       /* вернем длину строки  */
    }

Вот еще один вариант функции чтения строки: в нашем примере ее следует вызывать как
      fgetstr(buffer,sizeof(buffer),stdin);
Это подправленный вариант стандартной функции fgets (в ней строки @1  и  @2  обменяны
местами).

    char *fgetstr(char *s, int maxlen, register FILE *fp){
      register c; register char *cs = s;

      while(--maxlen > 0 && (c = getc(fp)) != EOF){
        if(c == '\n') break;  /* @1 */
        *cs++ = c;            /* @2 */
      }
      if(c == EOF && cs == s) return NULL;
      /* Заметьте, что при EOF строка s не меняется! */
      *cs = '\0'; return s;
    }

Исследуйте поведение этих функций, когда входная строка слишком длинная (длиннее max-
len).  Замечание: вместо нашей "рукописной" функции getstr() мы могли бы использовать
стандартную библиотечную функцию gets(buffer).

1.68.  Объясните, почему d стало отрицательным и почему %X печатает больше F,  чем  в
исходном числе? Пример выполнялся на 32-х битной машине.

    main(){
      unsigned short u = 65535;  /* 16 бит: 0xFFFF */
               short d = u;      /* 15 бит + знаковый бит */
      printf( "%X %d\n", d, d);  /* FFFFFFFF -1 */
    }

Указание: рассмотрите двоичное представление чисел (смотри приложение).  Какие приве-
дения типов здесь происходят?

1.69.  Почему 128 превратилось в отрицательное число?

    main()
    {
      /*signed*/ char c  = 128; /* биты: 10000000 */
      unsigned   char uc = 128;
      int  d = c;   /* используется 32-х битный int */
      printf( "%d %d %x\n", c, d, d );
      /*       -128 -128 ffffff80 */
           d = uc;
      printf( "%d %d %x\n", uc, d, d );
      /*       128 128 80       */
Предыдущая страница Следующая страница
1 2 3 4  5 6 7 8 9 10 11 12 13 14 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама