Главная · Поиск книг · Поступления книг · 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 ... 11 12 13 14 15 16 17  18 19 20 21 22 23 24 ... 87
                    или
            char s[] = "hello";
                    или
            char s[] = { "hello" };

В этом случае компилятор резервирует память для хранения  массива  и  расписывает  ее
байтами  начального значения.  Обратите внимание, что строка в двойных кавычках (если
ее рассматривать как массив букв) имеет длину на единицу больше, чем написано букв  в
строке,  поскольку в конце массива находится символ '\0' - признак конца, добавленный
компилятором.  Если бы мы написали

            char s[5] = "hello";

то компилятор сообщил бы об ошибке, поскольку длины массива (5)  недостаточно,  чтобы
разместить  6 байт.  В третьей строке примера написано s[], чтобы компилятор сам пос-
читал необходимую длину массива.
     Наконец, возможна ситуация, когда массив больше, чем хранящаяся  в  нем  строка.
Тогда "лишнее" место содержит какой-то мусор (в static-памяти изначально - байты \0).

      char s[12] = "hello";
      содержит:     h e l l o \0 ? ? ? ? ? ?

В программах текстовой обработки под "длиной строки" обычно понимают количество  букв
в  строке  НЕ  считая  закрывающий  байт '\0'. Именно такую длину считает стандартная
функция strlen(s).  Поэтому следует различать  такие  понятия  как  "(текущая)  длина
строки"  и  "длина  массива,  в котором хранится строка": sizeof(s).  Для написанного
выше примера эти значения равны соответственно 5 и 12.
     Следует также отличать массивы от указателей:

            char *sp = "bye bye";
            sp = "hello";

            printf("%d\n", ch);
равная  адресу  начала  массива),  а  указатель (переменная, хранящая адрес некоторой
области памяти).  Поскольку указатель -  это  переменная,  то  ее  значение  изменять
можно:  в  данном  случае  sp  сначала содержала адрес безымянного массива, в котором
находится "bye bye"; затем мы занесли  в  sp  адрес  безымянного  массива,  хранящего

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

строку "hello".  Здесь не происходит копирования массива, а происходит просто присва-
ивание переменной sp нового значения адреса.
     Предостережем от возможной неприятности:

            char d[5]; char s[] = "abcdefgh";
            strcpy(d, s);

Длины массива d просто не хватит для хранения такой длинной  строки.   Поскольку  это
ничем не контролируется (ни компилятором, ни самой strcpy, ни вами явным образом), то
при копировании строки "избыточные" байты запишутся после  массива  d  поверх  других
данных, которые будут испорчены.  Это приведет к непредсказуемым эффектам.
     Некоторые возможности для контроля за длиной строк-аргументов вам  дают  функции
strncpy(d,s,len);  strncat(d,s,len); strncmp(s1,s2,len).  Они пересылают (сравнивают)
не более, чем len первых символов строки s (строк s1, s2).  Посмотрите  в  документа-
цию!  Напишите функцию strncmp (сравнение строк по первым len символам), посмотрев на
функцию strncpy:

    char *strncpy(dst, src, n)
         register char *dst, *src;
         register int n;
    {    char *save;
         for(save=dst; --n >= 0; )
             if( !(*dst++ = *src++)){
                 while(--n >= 0)
                    *dst++ = '\0';
                 return save;
             }
         return save;
    }

Отметьте, что strncpy обладает одним неприятным свойством: если n <= strlen(src),  то
строка  dst  не  будет иметь на конце символа '\0', то есть будет находиться в некор-
ректном (не каноническом) состоянии.
     Ответ:

    int strncmp(register char *s1, register char *s2, register int n)
    {
            if(s1 == s2)
                    return(0);
            while(--n >= 0 && *s1 == *s2++)
                    if(*s1++ == '\0')
                            return(0);
            return((n < 0)? 0: (*s1 - *--s2));
    }

2.25.  В чем ошибка?

    #include   /* для putchar */
    char s[] = "We don't need no education";
    main(){ while(*s) putchar(*s++); }

Ответ: здесь s - константа, к ней неприменима операция ++.  Надо написать

    char *s = "We don't need no education";

сделав s указателем на безымянный маccив. Указатель уже можно изменять.

2.26.  Какие из приведенных конструкций обозначают одно и то же?

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

    char a[]  = "";         /* пустая строка */
    char b[]  = "\0";
    char c    = '\0';
    char z[]  = "ab";
    char aa[] = { '\0' };
    char bb[] = { '\0', '\0' };
    char xx[] = { 'a', 'b' };
    char zz[] = { 'a', 'b', '\0' };
    char *ptr = "ab";

2.27.  Найдите ошибки в описании символьной строки:

    main() {
       char mas[] = {'s', 'o', 'r', 't'};  /* "sort" ? */
       printf("%s\n", mas);
    }

Ответ: строка должна кончаться '\0' (в нашем случае printf не обнаружив символа конца
строки  будет  выдавать и байты, находящиеся в памяти после массива mas, т.е. мусор);
инициализированный массив не может быть автоматическим - требуется static:

    main() {
       static char mas[] = {'s', 'o', 'r', 't', '\0'};
    }

Заметим, что

    main(){    char *mas = "sort";   }

законно, т.к. сама строка здесь хранится в  статической  памяти,  а  инициализируется
лишь указатель на этот массив байт.

2.28.  В чем ошибка?  Программа собирается из двух файлов: a.c и b.c командой

           cc a.c b.c -o ab
    a.c                         b.c
    ---------------------------------------------------
    int n = 2;                  extern int n;
    char s[] = "012345678";     extern char *s;
    main(){                     f(){
      f();                         s[n] = '+';
      printf("%s\n", s );       }
    }

Ответ: дело в том, что типы (char *) - указатель, и char[] - массив, означают одно  и
то же только при объявлении формального параметра функции:

    f(char *arg){...}    f(char arg[]){...}

это будет локальная переменная, содержащая указатель на char (т.е.  адрес  некоторого
байта  в  памяти).   Внутри функции мы можем изменять эту переменную, например arg++.
Далее, и (char *) и char[] одинаково  используются,  например,  оба  эти  типа  можно
индексировать: arg[i]. Но вне функций они объявляют разные объекты!  Так char *p; это
скалярная переменная, хранящая адрес (указатель):

      --------      -------
    p:|   *--|----->| '0' | char
      --------      | '1' | char
                      ...

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

тогда как char a[20]; это адрес начала массива (а вовсе не переменная):

                    -------
                  a:| '0' | char
                    | '1' | char
                      ...

В нашем примере в файле b.c мы объявили внешний массив s как переменную.   В  резуль-
тате  компилятор  будет  интерпретировать начало массива s как переменную, содержащую
указатель на char.

                    -------
                  s:| '0' |   \  это будет воспринято как
                    | '1' |   /  адрес других данных.
                    | '2' |
                      ...

И индексироваться будет уже ЭТОТ адрес!  Результат  -  обращение  по  несуществующему
адресу.  То, что написано у нас, эквивалентно

    char s[]  = "012345678";
    char **ss = s;      /* s - как бы "массив указателей"  */
         /* первые байты s интерпретируются как указатель: */
    char  *p  = ss[0];
         p[2] = '+';

Мы же должны были объявить в b.c

    extern char s[];  /* размер указывать не требуется */

Вот еще один аналогичный пример, который пояснит вам, что происходит (а заодно  пока-
жет порядок байтов в long).  Пример выполнялся на IBM PC 80386, на которой

           sizeof(char *) = sizeof(long) = 4

    a.c                       b.c
    ---------------------------------------------------
    char s[20] = {1,2,3,4};   extern char *s;
    main(){                   f(){
                                /*печать указателя как long */
      f();                       printf( "%08lX\n", s );
    }                         }

печатается 04030201.

2.29.  Что напечатает программа?

      static char str1[ ]  = "abc";
      static char str2[4];

      strcpy( str2, str1 );
      /* можно ли написать str2 = str1; ? */

      printf( str1 == str2 ? "равно":"не равно" );

Как надо правильно сравнивать строки? Что на самом деле сравнивается  в  данном  при-
мере?
     Ответ: сравниваются адреса массивов, хранящих строки. Так

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

    char str1[2];
    char str2[2];
    main(){
      printf( str1 < str2 ? "<":">");
    }

печатает <<, а если написать

    char str2[2];
    char str1[2];

то напечатается >>.

2.30.  Напишите программу, спрашивающую ваше имя до тех пор, пока вы его правильно не
введете. Для сравнения строк используйте функцию strcmp() (ее реализация есть в главе
"Мобильность").

2.31.  Какие значения возвращает функция strcmp() в следующей программе?

    #include 
    main() {
      printf("%d\n", strcmp("abc", "abc")); /*   0 */
      printf("%d\n", strcmp("ab" , "abc")); /* -99 */
      printf("%d\n", strcmp("abd", "abc")); /*   1 */
      printf("%d\n", strcmp("abc", "abd")); /*  -1 */
      printf("%d\n", strcmp("abc", "abe")); /*  -2 */
    }

2.32.  В качестве итога предыдущих задач: помните, что в Си строки (а не адреса) надо
сравнивать как

    if( strcmp("abc", "bcd") <  0) ... ;
    if( strcmp("abc", "bcd") == 0) ... ;
               вместо
    if( "abc" <  "bcd" ) ... ;
    if( "abc" == "bcd" ) ... ;

и присваивать как

    char d[80], s[80];
    strcpy( d, s );      вместо    d = s;

2.33.  Напишите программу, которая сортирует по алфавиту и печатает следующие  ключе-
вые слова языка Си:

            int char double long
            for while if

2.34.  Вопрос не совсем про строки, скорее про цикл: чем плоха конструкция?

    char s[]  = "You're a smart boy, now shut up.";
    int i, len;
    for(i=0; i < strlen(s); i++)
             putchar(s[i]);

Ответ: в соответствии с семантикой Си цикл развернется примерно в

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

            i=0;
    LOOP:   if( !(i < strlen(s))) goto ENDLOOP;
              putchar(s[i]);
            i++;
            goto LOOP;
    ENDLOOP:         ;

Заметьте, что хотя длина строки s не меняется, strlen(s) вычисляется на КАЖДОЙ итера-
ции цикла, совершая лишнюю работу!  Борьба с этим такова:

    for(i=0, len=strlen(s); i < len; i++ )
             putchar(s[i]);
        или
    for(i=0, len=strlen(s); len > 0; i++, --len )
             putchar(s[i]);

Аналогично, в цикле

    while( i < strlen(s))...;

функция тоже будет вычисляться при каждой проверке условия! Это, конечно, относится к
любой функции, используемой в условии, а не только к strlen.  (Но, разумеется, случай
когда функция возвращает признак "надо ли продолжать  цикл"  -  совсем  другое  дело:
такая функция обязана вычисляться каждый раз).

2.35.  Что напечатает следующая программа?

    #include 
    main(){
        static char str[] = "До встречи в буфете";
        char *pt;

        pt = str; puts(pt); puts(++pt);
        str[7] = '\0'; puts(str); puts(pt);
        puts(++pt);
    }

2.36.  Что напечатает следующая программа?

    main() {
        static char name[] = "Константин";
        char *pt;
        pt = name + strlen(name);
        while(--pt >= name)
             puts(pt);
    }

2.37.  Что напечатает следующая программа?

        char str1[] = "abcdef";
        char str2[] = "xyz";
        main(){
            register char *a, *b;
            a = str1; b = str2;
            while( *b )
                   *a++ = *b++;
            printf( "str=%s a=%s\n", str1, a );
Предыдущая страница Следующая страница
1 ... 11 12 13 14 15 16 17  18 19 20 21 22 23 24 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама