1.30.

Напишите программу, которая при введении с клавиатуры буквы печатает на терминале ключевое слово, начинающееся с данной буквы. Например, при введении буквы 'b' печатает "break".

1.31.

Напишите программу, отгадывающую задуманное вами число в пределах от 1 до 200, пользуясь подсказкой с клавиатуры "=" (равно), "<" (меньше) и ">" (больше). Для угадывания числа используйте метод деления пополам.

1.32. Напишите программу, печатающую степени двойки


            1, 2, 4, 8, ...

Заметьте, что, начиная с некоторого n, результат становится отрицательным из-за переполнения целого.

1.33.

Напишите подпрограмму вычисления квадратного корня с использованием метода касательных (Ньютона):


       x(0)   =  a

                 1      a

       x(n+1) =  - * ( ----  + x(n))

                 2     x(n)

Итерировать, пока не будет | x(n+1) - x(n) | < 0.001

Внимание! В данной задаче массив не нужен. Достаточно хранить текущее и предыдущее значения x и обновлять их после каждой итерации.

1.34. Напишите программу, распечатывающую простые числа до 1000.


            1, 2, 3, 5, 7, 11, 13, 17, ...

    /*#!/bin/cc primes.c -o primes -lm

     *        Простые числа.

     */

    #include <stdio.h>

    #include <math.h>

    int debug = 0;

    /* Корень квадратный из числа по методу Ньютона */

    #define eps 0.0001

    double  sqrt (x) double x;

    {

        double  sq, sqold, EPS;

        if (x < 0.0)

            return -1.0;

        if (x == 0.0)

            return 0.0;  /* может привести к делению на 0 */

        EPS = x * eps;

        sq = x;

        sqold = x + 30.0;         /* != sq */

        while (fabs (sq * sq - x) >= EPS) {

        /*     fabs( sq - sqold )>= EPS    */

            sqold = sq;

            sq = 0.5 * (sq + x / sq);

        }

        return sq;

    }

    /* таблица прoстых чисел */

    int is_prime (t) register int    t; {

        register int    i, up;

        int             not_div;

        if (t == 2 || t == 3 || t == 5 || t == 7)

            return 1;               /* prime */

        if (t % 2 == 0 || t == 1)

            return 0;               /* composite */

        up = ceil (sqrt ((double) t)) + 1;

        i = 3;

        not_div = 1;

        while (i <= up && not_div) {

            if (t % i == 0) {

                if (debug)

                    fprintf (stderr, "%d поделилось на %d\n",

                                       t,               i);

                not_div = 0;

                break;

            }

            i += 2;  /*

                      * Нет смысла проверять четные,

                      * потому что если делится на 2*n,

                      * то делится и на 2,

                      * а этот случай уже обработан выше.

                      */

        }

        return not_div;

    }

    #define COL 6

    int     n;

    main (argc, argv) char **argv;

    {

        int     i,

                j;

        int     n;

        if( argc < 2 ){

            fprintf( stderr, "Вызов: %s число [-]\n", argv[0] );

            exit(1);

        }

        i = atoi (argv[1]); /* строка -> целое, ею изображаемое */

        if( argc > 2 ) debug = 1;

        printf ("\t*** Таблица простых чисел от 2 до %d ***\n", i);

        n = 0;

        for (j = 1; j <= i; j++)

            if (is_prime (j)){

                /* распечатка в COL колонок */

                printf ("%3d%s", j, n == COL-1 ? "\n" : "\t");

                if( n == COL-1 ) n = 0;

                else             n++;

            }

        printf( "\n---\n" );

        exit (0);

    }

1.35.

Составьте программу ввода двух комплексных чисел в виде A + B * I (каждое на отдельной строке) и печати их произведения в том же виде. Используйте scanf и printf.

Перед тем, как использовать scanf, проверьте себя: что неверно в нижеприведенном операторе?


    int x;

    scanf( "%d", x );

Ответ: должно быть написано "АДРЕС от x", то есть scanf( "%d", &x );

1.36.

Напишите подпрограмму вычисления корня уравнения f(x)=0 методом деления отрезка пополам. Приведем реализацию этого алгоритма для поиска целочисленного квадратного корня из целого числа (этот алгоритм может использоваться, например, в машинной графике при рисовании дуг):


    /* Максимальное unsigned long число */

    #define MAXINT (~0L)

    /* Определим имя-синоним для типа unsigned long */

    typedef unsigned long ulong;

    /* Функция, корень которой мы ищем: */

    #define FUNC(x, arg)  ((x) * (x) - (arg))

    /* тогда x*x - arg = 0 означает  x*x = arg, то есть

     * x = корень_квадратный(arg)     */

    /* Начальный интервал. Должен выбираться исходя из

     * особенностей функции FUNC */

    #define  LEFT_X(arg)  0

    #define RIGHT_X(arg) (arg > MAXINT)? MAXINT : (arg/2)+1;

    /* КОРЕНЬ КВАДРАТНЫЙ, округленный вниз до целого.

     * Решается по методу деления отрезка пополам:

     *    FUNC(x, arg) = 0;       x = ?
     */

    ulong i_sqrt( ulong arg ) {

       register ulong   mid,   /* середина интервала    */

                        rgt,   /* правый край интервала */

                        lft;   /* левый край интервала  */

       lft = LEFT_X(arg); rgt = RIGHT_X(arg);

       do{ mid = (lft + rgt + 1 )/2;

    /* +1 для ошибок округления при целочисленном делении */

           if( FUNC(mid, arg) > 0 ){

                  if( rgt == mid ) mid--;

                  rgt =  mid ;  /* приблизить правый край */

           } else lft =  mid ;  /* приблизить левый край  */

       } while( lft < rgt );

       return mid;

    }

    void main(){ ulong i;

       for(i=0; i <= 100; i++)

           printf("%ld -> %lu\n", i, i_sqrt(i));

    }

Использованное нами при объявлении переменных ключевое слово register означает, что переменная является ЧАСТО ИСПОЛЬЗУЕМОЙ, и компилятор должен попытаться разместить ее на регистре процессора, а не в стеке (за счет чего увеличится скорость обращения к этой переменной). Это слово используется как


    register тип переменная;

    register переменная; /* подразумевается тип int */

От регистровых переменных нельзя брать адрес: &переменная ошибочно.

1.37. Напишите программу, вычисляющую числа треугольника Паскаля и печатающую их в виде треугольника.


            C(0,n)   = C(n,n)   = 1          n = 0...

            C(k,n+1) = C(k-1,n) + C(k,n)     k = 1..n

            n - номер строки

В разных вариантах используйте циклы, рекурсию.

1.38.

Напишите функцию вычисления определенного интеграла методом Монте-Карло. Для этого вам придется написать генератор случайных чисел. Си предоставляет стандартный датчик ЦЕЛЫХ равномерно распределенных псевдослучайных чисел: если вы хотите получить целое число из интервала [A..B], используйте


    int x = A + rand() % (B+1-A);

Чтобы получать разные последовательности следует задавать некий начальный параметр последовательности (это называется "рандомизация") при помощи


    srand( число ); /* лучше нечетное */

Чтобы повторить одну и ту же последовательность случайных чисел несколько раз, вы должны поступать так:


    srand(NBEG); x=rand(); ... ; x=rand();

    /* и повторить все сначала */

    srand(NBEG); x=rand(); ... ; x=rand();

Используемый метод получения случайных чисел таков:

    static unsigned long int next = 1L;

    int rand(){

      next = next * 1103515245 + 12345;

      return ((unsigned int)(next/65536) % 32768);

    }

    void srand(seed) unsigned int seed;

    {     next = seed;    }

Для рандомизации часто пользуются таким приемом:

    char t[sizeof(long)];

    time(t); srand(t[0] + t[1] + t[2] + t[3] + getpid());

1.39. Напишите функцию вычисления определенного интеграла по методу Симпсона.


    /*#!/bin/cc $* -lm

     * Вычисление интеграла по методу Симпсона

     */

    #include <math.h>

    extern double integral(), sin(), fabs();

    #define PI 3.141593

    double myf(x) double x;

    {      return sin(x / 2.0);      }

    int niter;  /* номер итерации */

    void main(){

            double integral();

            printf("%g\n", integral(0.0, PI, myf, 0.000000001));

            /* Заметьте, что myf, а не myf().

             * Точное значение интеграла равно 2.0

             */

            printf("%d итераций\n", niter );

    }

    double integral(a, b, f, eps)

            double a, b;    /* концы отрезка */

            double eps;     /* требуемая точность */

            double (*f)();  /* подынтегральная функция */

    {

            register long i;

            double fab = (*f)(a) + (*f)(b); /* сумма на краях */

            double h, h2;   /* шаг и удвоенный шаг */

            long n, n2;     /* число точек разбиения и оно же удвоенное */

            double Sodd, Seven;  /* сумма значений f в нечетных и в

                                    четных точках */

            double S, Sprev;/* значение интеграла на данной

                               и на предыдущей итерациях */

            double x;       /* текущая абсцисса */

            niter = 0;

            n = 10L;        /* четное число */

            n2 = n * 2;

            h = fabs(b - a) / n2;   h2 = h * 2.0;

            /* Вычисляем первое приближение */

            /* Сумма по нечетным точкам: */

            for( Sodd = 0.0, x = a+h, i = 0;

                                      i < n;

                                      i++, x += h2 )

                    Sodd += (*f)(x);

            /* Сумма по четным точкам: */

            for( Seven = 0.0, x = a+h2, i = 0;

                                        i < n-1;

                                        i++, x += h2 )

                    Seven += f(x);

            /* Предварительное значение интеграла: */

            S = h / 3.0 * (fab + 4.0 * Sodd + 2.0 * Seven );

            do{

                    niter++;

                    Sprev = S;

                    /* Вычисляем интеграл с половинным шагом */

                    h2 = h;      h /= 2.0;

                    if( h == 0.0 ) break;   /* потеря значимости */

                    n  = n2;     n2 *= 2;

                    Seven = Seven + Sodd;

                    /* Вычисляем сумму по новым точкам: */

                    for( Sodd = 0.0, x = a+h, i = 0;

                                              i < n;

                                              i++, x += h2 )

                    Sodd += (*f)(x);

                    /* Значение интеграла */

                    S = h / 3.0 * (fab + 4.0 * Sodd + 2.0 * Seven );

            } while( niter < 31 && fabs(S - Sprev) / 15.0 >= eps );

            /* Используем условие Рунге для окончания итераций */

            return ( 16.0 * S - Sprev ) / 15.0 ;

            /* Возвращаем уточненное по Ричардсону значение */

    }

1.40. Где ошибка?


    struct time_now{

      int  hour, min, sec;

    } X = { 13, 08, 00 }; /* 13 часов 08 минут 00 сек.*/

Ответ: 08 - восьмеричное число (так как начинается с нуля)! А в восьмеричных числах цифры 8 и 9 не бывают.

1.41. Дан текст:


       int i = -2;

       i <<= 2;

       printf("%d\n", i); /* печать сдвинутого i : -8 */

       i >>= 2;

       printf("%d\n", i); /* печатается -2 */

Закомментируем две строки (исключая их из программы):

       int i = -2;

       i <<= 2;

    /*

       printf("%d\n", i); /* печать сдвинутого i : -8 */

       i >>= 2;

    */

       printf("%d\n", i); /* печатается -2 */

Почему теперь возникает ошибка? Указание: где кончается комментарий?

Ответ: Си не допускает вложенных комментариев. Вместо этого часто используются конструкции вроде:


    #ifdef COMMENT

      ... закомментированный текст ...

    #endif /*COMMENT*/

и вроде

    /**/ printf("here");/* отладочная выдача включена  */

    /*   printf("here");/* отладочная выдача выключена */

или

    /* выключено();    /**/

       включено();     /**/

А вот дешевый способ быстро исключить оператор (с возможностью восстановления) конец комментария занимает отдельную строку, что позволяет отредактировать такой текст редактором почти не сдвигая курсор:


       /*printf("here");

        */

1.42. Почему программа печатает неверное значение для i2 ?


    int main(int argc, char *argv[]){

            int i1, i2;

            i1 = 1;         /* Инициализируем i1 /

            i2 = 2;         /* Инициализируем i2 */

            printf("Numbers %d %d\n", i1, i2);

            return(0);

    }

Ответ: в первом операторе присваивания не закрыт комментарий - весь второй оператор присваивания полностью проигнорировался! Правильный вариант:

    int main(int argc, char *argv[]){

            int i1, i2;

            i1 = 1;         /* Инициализируем i1 */

            i2 = 2;         /* Инициализируем i2 */

            printf("Numbers %d %d\n", i1, i2);

            return(0);

    }

1.43. А вот "шальной" комментарий.


    void main(){

            int n    = 10;

            int *ptr = &n;

            int x, y = 40;

            x = y/*ptr     /* должно быть 4 */  + 1;

            printf( "%d\n", x );    /* пять */

            exit(0);

    }

    /* или такой пример из жизни - взят из переписки в Relcom */

    ...

    cost = nRecords/*pFactor     /* divided by Factor, and  */

           + fixMargin;          /* plus the precalculated  */

    ...

Результат непредсказуем. Дело в том, что y/*ptr превратилось в начало комментария!

Поэтому бинарные операции принято окружать пробелами.


    x = y / *ptr   /* должно быть 4 */  + 1;

1.44.

Найдите ошибки в директивах препроцессора Си * (вертикальная черта обозначает левый край файла).


            |

            | #include <stdio.h>

            |#include  < sys/types.h >

            |#   define inc (x) ((x) + 1)

            |#define N 12;

            |#define X -2

            |

            |...  printf( "n=%d\n", N );

            |...  p = 4-X;

Ответ: в первой директиве стоит пробел перед #. Диез должен находиться в первой позиции строки. Во второй директиве в <> находятся лишние пробелы, не относящиеся к имени файла - препроцессор не найдет такого файла! В данном случае "красота" пошла во вред делу. В третьей - между именем макро inc и его аргументом в круглых скобках (x) стоит пробел, который изменяет весь смысл макроопределения: вместо макроса с параметром inc(x) мы получаем, что слово inc будет заменяться на (x)((x)+1). Заметим однако, что пробелы после # перед именем директивы вполне допустимы. В четвертом случае показана характерная опечатка - символ ; после определения. В результате написанный printf() заменится на


            printf( "n=%d\n", 12; );

где лишняя ; даст синтаксическую ошибку.

В пятом случае ошибки нет, но нас ожидает неприятность в строке p=4-X; которая расширится в строку p=4--2; являющуюся синтаксически неверной. Чтобы избежать подобной ситуации, следовало бы написать


            p = 4 - X;  /* через пробелы */

но еще проще (и лучше) взять макроопределение в скобки:

            #define X (-2)

1.45. Напишите функцию max(x, y), возвращающую большее из двух значений. Напишите аналогичное макроопределение. Напишите макроопределения min(x, y) и abs(x) (abs модуль числа). Ответ:


    #define abs(x)   ((x)  <  0   ? -(x) : (x))

    #define min(x,y) (((x) < (y)) ?  (x) : (y))

Зачем x взят в круглые скобки (x)? Предположим, что мы написали

    #define abs(x)  (x < 0 ? -x : x )

                вызываем

    abs(-z)                  abs(a|b)

                получаем

    (-z < 0 ? --z : -z )     (a|b < 0 ? -a|b : a|b )

У нас появилась "дикая" операция --z; а выражение a|b<0 соответствует a|(b<0), с совсем другим порядком операций! Поэтому заключение всех аргументов макроса в его теле в круглые скобки позволяет избежать многих неожиданных проблем. Придерживайтесь этого правила!

Вот пример, показывающий зачем полезно брать в скобки все определение:


    #define div(x, y)     (x)/(y)

При вызове

         z = sizeof div(1, 2);

             превратится в

         z = sizeof(1) / (2);

что равно sizeof(int)/2, а не sizeof(int). Вариант

    #define div(x, y) ((x) / (y))

будет работать правильно.

1.46.

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


    int sqr(int x){ return x  *  x; }

    #define SQR(x)       ((x) * (x))

    main(){ int y=2, z;

       z = sqr(y++); printf("y=%d z=%d\n", y, z);

       y = 2;

       z = SQR(y++); printf("y=%d z=%d\n", y, z);

    }

Вызов функции sqr печатает "y=3 z=4", как мы и ожидали. Макрос же SQR расширяется в

       z = ((y++) * (y++));

и результатом будет "y=4 z=6", где z совсем не похоже на квадрат числа 2.

1.47. ANSI ANSI препроцессор** языка Си имеет оператор ## - "склейка лексем":


    #define VAR(a, b)       a ## b

    #define CV(x)           command_ ## x

    main(){

      int VAR(x, 31) = 1;

      /* превратится в int x31 = 1; */

      int CV(a) = 2; /* даст int command_a = 2; */

      ...

    }

Старые версии препроцессора не обрабатывают такой оператор, поэтому раньше использовался такой трюк:

    #define VAR(a, b)       a/**/b

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

1.48.

Напишите программу, распечатывающую максимальное и минимальное из ряда чисел, вводимых с клавиатуры. Не храните вводимые числа в массиве, вычисляйте max и min сразу при вводе очередного числа!


    #include <stdio.h>

    main(){

      int max, min, x, n;

      for( n=0; scanf("%d", &x) != EOF; n++)

            if( n == 0 ) min = max = x;

            else{

               if( x > max ) max = x;

               if( x < min ) min = x;

            }

      printf( "Ввели %d чисел: min=%d max=%d\n",

                     n,            min,   max);

    }

Напишите аналогичную программу для поиска максимума и минимума среди элементов массива, изначально min=max=array[0];

1.49.

Напишите программу, которая сортирует массив заданных чисел по возрастанию (убыванию) методом пузырьковой сортировки. Когда вы станете более опытны в Си, напишите сортировку методом Шелла.


    /*

     * Сортировка по методу Шелла.

     * Сортировке подвергается массив указателей на данные типа obj.

     *      v------.-------.------.-------.------0

     *             !       !      !       !

     *             *       *      *       *

     *            элементы типа obj

     * Программа взята из книги Кернигана и Ритчи.

     */

    #include <stdio.h>

    #include <string.h>

    #include <locale.h>

    #define obj char

    static shsort (v,n,compare)

    int n;              /* длина массива */

    obj *v[];           /* массив указателей */

    int (*compare)();   /* функция сравнения соседних элементов */

    {

            int g,      /* расстояние, на котором происходит сравнение */

                i,j;    /* индексы сравниваемых элементов */

            obj *temp;

            for( g = n/2 ; g > 0  ; g /= 2 )

            for( i = g   ; i < n  ; i++    )

            for( j = i-g ; j >= 0 ; j -= g )

            {

                    if((*compare)(v[j],v[j+g]) <= 0)

                        break;      /* уже в правильном порядке */

                    /* обменять указатели */

                    temp = v[j]; v[j] = v[j+g]; v[j+g] = temp;

                    /* В качестве упражнения можете написать

                     * при помощи curses-а программу,

                     * визуализирующую процесс сортировки:

                     * например, изображающую эту перестановку

                     * элементов массива */

            }

    }

    /* сортировка строк */

    ssort(v) obj **v;

    {

            extern less();  /* функция сравнения строк */

            int len;

            /* подсчет числа строк */

            len=0;

            while(v[len]) len++;

            shsort(v,len,less);

    }

    /* Функция сравнения строк.

     * Вернуть целое меньше нуля, если a <  b

     *                      ноль, если a == b

     *               больше нуля, если a >  b

     */

    less(a,b) obj *a,*b;

    {

            return strcoll(a,b);

            /* strcoll - аналог strcmp,

             * но с учетом алфавитного порядка букв.

             */

    }

    char *strings[] = {

            "Яша", "Федя", "Коля",

            "Гриша", "Сережа", "Миша",

            "Андрей Иванович", "Васька",

            NULL

    };

    int main(){

            char **next;

            setlocale(LC_ALL, "");

            ssort( strings );

            /* распечатка */

            for( next = strings ; *next ; next++ )

                    printf( "%s\n", *next );

            return 0;

    }

* Препроцессор Си - это программа /lib/cpp

** ANSI - American National Standards Institute, разработавший стандарт на язык Си и его окружение.

[Назад][Содержание][Вперед]

Используются технологии uCoz