#include <stdio.h>
    main(){
        printf("Hello, world\n");
    }
Ответ: раз не объявлено иначе, функция main считается возвращающей целое значение (int). Но функция main не возвращает ничего - в ней просто нет оператора return.
Корректно было бы так:
    #include <stdio.h>
    main(){
        printf("Hello, world\n");
        return 0;
    }
или
    #include <stdio.h>
    void main(){
        printf("Hello, world\n");
        exit(0);
    }
а уж совсем корректно - так:
    #include <stdio.h>
    int main(int argc, char *argv[]){
        printf("Hello, world\n");
        return 0;
    }
        #include studio.h
        main
        {
            int i
            i := 43
            print ('В году i недель')
        }
            int n;
            n = 2;
            printf ("%d + %d = %d\n", n, n, n + n);
            if( x > 2 )
            then    x = 2;
            if  x < 1
                    x = 1;
Ответ: в Си нет ключевого слова then, условия в операторах if, while должны браться в ()-скобки.
Напишите программу, печатающую ваше имя, место работы и адрес. В первом варианте программы используйте библиотечную функцию printf, а во втором - puts.
Составьте программу с использованием следующих постфиксных и префиксных операций:
            a = b = 5
            a + b
            a++ + b
            ++a + b
            --a + b
            a-- + b
Распечатайте полученные значения и проанализируйте результат.
         for(INIT; CONDITION; INCR)
                 BODY
                 INIT;
         repeat:
                 if(CONDITION){
                         BODY;
                 cont:
                         INCR;
                         goto repeat;
                 }
         out:    ;
Цикл while
         while(COND)
                 BODY
         cont:
         repeat:
                 if(CONDITION){
                         BODY;
                         goto repeat;
                 }
         out:    ;
Цикл do
         do
                 BODY
         while(CONDITION)
         cont:
         repeat:
                 BODY;
                 if(CONDITION) goto repeat;
         out:    ;
В операторах цикла внутри тела цикла BODY могут присутствовать операторы break и continue; которые означают на наших схемах следующее:
         #define break     goto out
         #define continue  goto cont
              *
              **
              ***
              ****
              *****
используя цикл for. Введите переменную, значением которой является размер катета треугольника.
Напишите операторы Си, которые выдают строку длины WIDTH, в которой сначала содержится x0 символов '-', затем w символов '*', и до конца строки - вновь символы '-'. Ответ:
    int x;
    for(x=0; x < x0;     ++x) putchar('-');
    for(   ; x < x0 + w; x++) putchar('*');
    for(   ; x < WIDTH ; ++x) putchar('-');
    putchar('\n');
либо
    for(x=0; x < WIDTH; x++)
            putchar( x < x0     ? '-' :
                     x < x0 + w ? '*' :
                                  '-' );
    putchar('\n');
                    *
                   ***
                  *****
                 *******
                *********
Ответ:
    /* Треугольник из звездочек */
    #include <stdio.h>
    /* Печать n символов c */
    printn(c, n){
            while( --n >= 0 )
                    putchar(c);
    }
    int lines = 10;         /* число строк треугольника */
    void main(argc, argv) char *argv[];
    {
            register int nline;  /* номер строки */
            register int naster; /* количество звездочек в строке */
            register int i;
            if( argc > 1 )
                    lines = atoi( argv[1] );
            for( nline=0; nline < lines ; nline++ ){
                    naster = 1 + 2 * nline;
                    /* лидирующие пробелы */
                    printn(' ', lines-1  - nline);
                    /* звездочки */
                    printn('*', naster);
                    /* перевод строки */
                    putchar( '\n' );
            }
            exit(0);        /* завершение программы */
    }
    main(){  /* печать фразы 10 раз */
       int i;
       while(i < 10){
           printf("%d-ый раз\n", i+1);
           i++;
       }
    }
Ответ: автоматическая переменная i не была проинициализирована и содержит не 0, а какое-то произвольное значение. Цикл может выполниться не 10, а любое число раз (в том числе и 0 по случайности). Не забывайте инициализировать переменные, возьмите описание с инициализацией за правило!
       int i = 0;
Если бы переменная i была статической, она бы имела начальное значение 0.
В данном примере было бы еще лучше использовать цикл for, в котором все операции над индексом цикла собраны в одном месте - в заголовке цикла:
    for(i=0; i < 10; i++) printf(...);
Вспомогательные переменные, не несущие смысловой нагрузки (вроде счетчика повторений цикла, не используемого в самом теле цикла) принято по традиции обозначать однобуквенными именами, вроде i, j. Более того, возможны даже такие курьезы:
    main(){
      int  _ ;
      for( _ = 0; _ < 10; _++) printf("%d\n", _ );
    }
основанные на том, что подчерк в идентификаторах - полноправная буква.
            main(){
                    int x = 12;
                    printf( "x=%d\n" );
                    int y;
                    y = 2 * x;
                    printf( "y=%d\n", y );
            }
Комментарий: в теле функции все описания должны идти перед всеми выполняемыми операторами (кроме операторов, входящих в состав описаний с инициализацией). Очень часто после внесения правок в программу некоторые описания оказываются после выполняемых операторов. Именно поэтому рекомендуется отделять строки описания переменных от выполняемых операторов пустыми строками (в этой книге это часто не делается для экономии места).
    int n;
    n = 12;
    main(){
            int y;
            y = n+2;
            printf( "%d\n", y );
    }
Ответ: выполняемый оператор n=12 находится вне тела какой-либо функции. Следует внести его в main() после описания переменной y, либо переписать объявление перед main() в виде
    int n = 12;
В последнем случае присваивание переменной n значения 12 выполнит компилятор еще во время компиляции программы, а не сама программа при своем запуске. Точно так же происходит со всеми статическими данными (описанными как static, либо расположенными вне всех функций); причем если их начальное значение не указано явно - то подразумевается 0 ('\0', NULL, ""). Однако нулевые значения не хранятся в скомпилированном выполняемом файле, а требуемая "чистая" память расписывается при старте программы.
    TYPE x = выражение;
является (почти) эквивалентом для
    TYPE x;         /* описание */
    x = выражение;  /* вычисление начального значения */
Рассмотрим пример:
    #include <stdio.h>
    extern double sqrt();   /* квадратный корень */
    double x   = 1.17;
    double s12 = sqrt(12.0);            /* #1 */
    double y   = x * 2.0;               /* #2 */
    FILE  *fp  = fopen("out.out", "w"); /* #3 */
    main(){
      double ss = sqrt(25.0) + x;       /* #4 */
      ...
    }
Строки с метками #1, #2 и #3 ошибочны. Почему?
Ответ: при инициализации статических данных (а s12, y и fp таковыми и являются, так как описаны вне какой-либо функции) выражение должно содержать только константы, поскольку оно вычисляется КОМПИЛЯТОРОМ. Поэтому ни использование значений переменных, ни вызовы функций здесь недопустимы (но можно брать адреса от переменных).
В строке #4 мы инициализируем автоматическую переменную ss, т.е. она отводится уже во время выполнения программы. Поэтому выражение для инициализации вычисляется уже не компилятором, а самой программой, что дает нам право использовать переменные, вызовы функций и.т.п., то есть выражения языка Си без ограничений.
Напишите программу, реализующую эхо-печать вводимых символов. Программа должна завершать работу при получении признака EOF. В UNIX при вводе с клавиатуры признак EOF обычно обозначается одновременным нажатием клавиш CTRL и D (CTRL чуть раньше), что в дальнейшем будет обозначаться CTRL/D; а в MS DOS - клавиш CTRL/Z. Используйте getchar() для ввода буквы и putchar() для вывода.
Напишите программу, подсчитывающую число символов поступающих со стандартного ввода. Какие достоинства и недостатки у следующей реализации:
    #include <stdio.h>
    main(){ double cnt = 0.0;
            while (getchar() != EOF) ++cnt;
            printf("%.0f\n", cnt );
    }
Ответ: и достоинство и недостаток в том, что счетчик имеет тип double. Достоинство можно подсчитать очень большое число символов; недостаток - операции с double обычно выполняются гораздо медленнее, чем с int и long (до десяти раз), программа будет работать дольше. В повседневных задачах вам вряд ли понадобится иметь счетчик, отличный от long cnt; (печатать его надо по формату "%ld").
Составьте программу перекодировки вводимых символов со стандартного ввода по следующему правилу:
            a -> b
            b -> c
            c -> d
            ...
            z -> a
     другой символ -> *
Коды строчных латинских букв расположены подряд по возрастанию.
Составьте программу перекодировки вводимых символов со стандартного ввода по следующему правилу:
            B -> A
            C -> B
            ...
            Z -> Y
     другой символ -> *
Коды прописных латинских букв также расположены по возрастанию.
Напишите программу, печатающую номер и код введенного символа в восьмеричном и шестнадцатеричном виде. Заметьте, что если вы наберете на вводе строку символов и нажмете клавишу ENTER, то программа напечатает вам на один символ больше, чем вы набрали. Дело в том, что код клавиши ENTER, завершившей ввод строки - символ '\n' тоже попадает в вашу программу (на экране он отображается как перевод курсора в начало следующей строки!).
Разберитесь, в чем состоит разница между символами '0' (цифра нуль) и '\0' (нулевой байт). Напечатайте
      printf( "%d %d %c\n", '\0', '0', '0' );
Поставьте опыт: что печатает программа?
    main(){
            int c = 060;  /* код символа '0' */
            printf( "%c %d %o\n", c, c, c);
    }
Почему печатается 0 48 60?  Теперь напишите вместо
    int c = 060;
строчку
    char c = '0';
    #include <stdio.h>
    void main(){
            printf("ab\0cd\nxyz");
            putchar('\n');
    }
Запомните, что '\0' служит признаком конца строки в памяти, а '\n' - в файле.  Что  в строке "abcd\n" на конце неявно уже расположен нулевой байт:
    'a','b','c','d','\n','\0'
Что строка "ab\0cd\nxyz" - это
    'a','b','\0','c','d','\n','x','y',z','\0'
Что строка "abcd\0" - избыточна, поскольку будет иметь на конце два нулевых байта (что не вредно, но зачем?). Что printf печатает строку до нулевого байта, а не до закрывающей кавычки. Программа эта напечатает ab и перевод строки.
Вопрос: чему равен sizeof("ab\0cd\nxyz")? Ответ: 10.
Напишите программу, шифрующую текст файла путем замены значения символа (например, значение символа C заменяется на C+1 или на ~C ).
© Copyright А. Богатырев, 1992-95  
     Си в UNIX
Назад | Содержание | Вперед
