Перейти на главную   
  helloworld.ru - документация и книги по программированию  
helloworld.ru - документация и книги по программированию
    главная     хостинг    
Поиск по сайту:  
Смотрите также
Языки программирования
C#
MS Visual C++
Borland C++
C++ Builder
Visual Basic
Quick Basic
Turbo Pascal
Delphi
JavaScript
Java
PHP
Perl
Assembler
AutoLisp
Fortran
Python
1C

Интернет-технологии
HTML
VRML
HTTP
CGI
FTP
Proxy
DNS
протоколы TCP/IP
Apache

Web-дизайн
HTML
Дизайн
VRML
PhotoShop
Cookie
CGI
SSI
CSS
ASP
PHP
Perl

Программирование игр
DirectDraw
DirectSound
Direct3D
OpenGL
3D-графика
Графика под DOS

Алгоритмы
Численные методы
Обработка данных

Сис. программирование
Драйверы

Базы данных
MySQL
SQL

Другое

Хостинг


Друзья
demaker.ru
Реклама

Лучший хостинг. Аренда серверов




helloworld.ru

                           СТРОКА заключается в двойные кавычки
                           (не путать с одиночными для putchar().
                         */
                        printf("кот");
                        nanimal++;      /* посчитать еще одного зверя */

                        /* и проверить - не надо ли перейти на новую строку ? */
                        checkIfWeHaveToBreakLine();
                }
                for(i=0; i < CATS; i++){
                        printf("кошка");
                        nanimal++;      /* посчитать еще одного зверя */

                        /* и проверить - не надо ли перейти на новую строку ? */
                        checkIfWeHaveToBreakLine();
                }
        }
        /* putchar('\n'); */
}
     16.c

/*
        Та же задача, но еще надо печатать номер каждого зверя.
        Ограничимся пятью строками.
*/

#include       /* магическая строка */

int TOMCATS     = 3;            /* три кота  */
int CATS        = 2;            /* две кошки */
int ANIMALS_PER_LINE    = 6;    /* шесть зверей в каждой строке */
int LINES       = 5;            /* число выводимых строк */
int ANIMALS;                    /* общее число зверей */

int nth_in_line = 0;            /* номер зверя в текущей строке */

void checkIfWeHaveToBreakLine(){
        nth_in_line++;

        if(nth_in_line == ANIMALS_PER_LINE){
                putchar('\n');
                nth_in_line = 0;
        } else
                printf("\t\t");         /* @ */

        /* Одинокий оператор может обойтись без {...} вокруг него */
}

void main(){
        int nanimal;
        int i;

        ANIMALS = ANIMALS_PER_LINE * LINES;
        nanimal = 0;

        while(nanimal < ANIMALS){

                for(i=0; i < TOMCATS; i++){
                /* Формат %d выводит значение переменной типа int
                   в виде текстовой строки.
                   Сама переменная должна быть в списке после формата
                   (список - это перечисление переменных через запятую).
                   Переменных ИЛИ выражений (формул).

                   Давайте выводить по ДВЕ табуляции --
                   это место отмечено в функции checkIfWeHaveToBreakLine()
                   как @.

                   Еще раз внимание - один символ мы выводим как
                        putchar('a');
                   Несколько символов - как
                        printf("abcdef");

                   Одиночные кавычки - для одной буквы.
                   Двойные кавычки   - для нескольких.
                */

                        printf("кот%d", nanimal);
                        nanimal++;

                        checkIfWeHaveToBreakLine();
                }
                for(i=0; i < CATS; i++){
                        printf("кошка%d", nanimal);
                        nanimal++;

                        checkIfWeHaveToBreakLine();
                }
        }
}
     17.c

/* Задача: напечатать корни из чисел от 1 до 100.

   Новая информация:
        Нам понадобится новый тип данных - ДЕЙСТВИТЕЛЬНЫЕ ЧИСЛА.
        Это числа, имеющие дробную часть (после точки).
        Как мы уже знаем, целые - это   int.
                          буква - это   char.
                          действительное число - это double.
        (есть еще слово float, но им пользоваться не рекомендуется).

   Для вычисления корня используется итерационный алгоритм Герона.

                q = корень из x;

                q[0]   := x;
                q[n+1] := 1/2 * ( q[n] + x/q[n] );

   Главное тут не впасть в ошибку, не клюнуть на q[n] и не
   завести массив. Нам не нужны результаты каждой итерации,
   нам нужен только конечный ответ. Поэтому нам будет вполне
   достаточно ОДНОЙ, но изменяющейся в цикле, ячейки q.
*/

#include

/* Еще одно новое ключевое слово - const. Обозначает константы.
   В отличие от переменных, такие имена нельзя изменять.
   То есть, если где-то потом попробовать написать epsilon = ... ;
   то это будет ошибкой.
 */
const double epsilon = 0.0000001;       /* точность вычислений */

/* Функция вычисления модуля числа */
double doubleabs(double x){
        if(x < 0) return -x;
        else      return x;
}

/* Функция вычисления квадратного корня */
double sqrt(double x){

        double sq = x;

        /* Такая конструкция есть просто склейка двух строк:
                double sq;
                sq = x;
           Называется это "объявление переменной с инициализацией".
         */

        while(doubleabs(sq*sq - x) >= epsilon){
                sq = 0.5 * (sq + x/sq);
        }
        return sq;
}

void main() {
        int n;

        for(n=1; n <= 100; n++)
                printf("sqrt(%d)=%lf\n",
                             n,  sqrt((double) n)
                      );

}

/*
   Здесь в операторе printf() мы печатаем ДВА выражения.
        ФОРМАТ          ЗНАЧЕНИЕ
        ------          --------
        %d      --      n
        %lf     --      sqrt((double) n)

        По формату %d печатаются значения типа int.
        По формату %c печатаются значения типа char.
        По формату %lf (или %g) печатаются значения типа double.

   Что значит "напечатать значение выражения sqrt(xxx)" ?
   Это значит:
        - вызвать функцию sqrt() с аргументом, равным xxx;
        - вычислить ее;
        - возвращенное ею значение напечатать по формату %lf,
          то есть как действительное число.

   Заметьте, что тут возвращаемое значение НЕ присваивается
   никакой переменной, мы не собираемся его хранить.

   Точно так же, как в операторе x = 12 + 34;
   12 и 34 не хранятся ни в каких переменных,
   а оператор

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

   печатает ЧИСЛО 12, а не переменную.

   Точно так же, как можно писать

        double z;

        z = sqrt(12) + sqrt(23);

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

        z = sqrt( sqrt(81));

        (корень из корня из 81 --> даст 3)

   Далее, что означает конструкция (double) n   ?
   Функция sqrt() требует аргумента типа double.
   Мы же предлагаем ей целый аргумент

        int n;

   Целые и действительные числа представлены в памяти
   машины ПО-РАЗНОМУ,
   поэтому числа

        12   и   12.0  хранятся в памяти ПО-РАЗНОМУ.

   Машина умеет преобразовывать целые числа в действительные
   и наоборот, надо только сказать ей об этом.
   Оператор (double) x
   называется "приведение типа к double".

   Заметим, что часто преобразование типа
   выполняется автоматически.

   Так, например, при сложении int и double
   int автоматически приводится к double, и результат
   имеет тип double.

        int    var1;
        double var2, var3;

        var1 = 2;
        var2 = 2.0;
        var3 = var1 + var2;

   что означает на самом деле

        var3 = (double) var1 + var2;

   var3 станет равно 4.0

   Более того, к примеру тип char - это тоже ЦЕЛЫЕ ЧИСЛА из интервала
   0...255. Каждая буква имеет код от 0 до 255.

*/
      * 18_POINTERS.txt *

УКАЗАТЕЛИ
=========
void f(int x){
        x = 7;
}

main(){
        int y = 17;
        f(y);
        printf("y=%d\n", y);       /* печатает: y=17 */
}

В аргументе x передаётся КОПИЯ значения y,
поэтому x=7; не изменяет значения у.
Как все же сделать, чтобы вызываемая функция
могла изменять значение переменной?

Отбросим два способа:

        - объявление y как глобальной
          (много глобальных переменных - плохой стиль),

        - y=f(y);
          (а что если надо изменить МНОГО переменных?
          return, к несчастью, может вернуть лишь одно значение).

Используется новая для нас конструкция: УКАЗАТЕЛЬ.
  ------------------------------------------------------------------------

Пример (@)

void f(int *ptr){       /* #2 */
        *ptr = 7;       /* #3 */
}

main (){
        int y=17;

        f(&y);          /* #1 */
        printf("y=%d\n", y);       /* печатает: y=7 */
}

Ну как, нашли три отличия от исходного текста?
  ------------------------------------------------------------------------

Мы вводим две новые конструкции:

        &y              "указатель на переменную y" или
                        "адрес переменной y"

        *ptr            означает "разыменование указателя ptr"
                        (подробнее - позже)

        int *ptr;       означает объявление переменной ptr,
                        которая может содержать в себе
                        указатель на переменную,
                        хранящую int-число.

Для начала определим, что такое указатель.

        int var1, var2, z;      /* целочисленные переменные */
        int *pointer;           /* указатель на целочисленную переменную */

        var1    = 12;
        var2    = 43;
        pointer = &var1;

Мы будем изображать указатель в виде СТРЕЛКИ;
это хороший прием и при практическом программировании.
        ________
       /pointer/
     _/_______/_
     | значение|        Переменная, хранящая указатель
     |   есть  |        (адрес другой переменной)
     |         |
     |  &var1  |
     |         |
     |_______|_|
             |
             |&var1     - сам указатель на var1 -
             |            "стрелка, указывающая на переменную var1".
             V            Она обозначается &var1
        ________
       / var1  /
     _/_______/_
     | значение|
     |   есть  |
     |    12   |
     |_________|

Таким образом, УКАЗАТЕЛЬ - это "стрелка, указывающая на некий ящик-переменную".
Начало этой стрелки можно (в свою очередь) хранить в какой-нибудь переменной.

При этом, если стрелка указывает на переменную типа int,
то тип переменной, хранящей начало стрелки, есть int *

Если типа char, то тип - char *

АДРЕС (указатель на) можно взять только от переменной или элемента массива,
но не от выражения.

        int x;
        int arr[5];

        Законно     &x          стрелка на ящик "x"
                    & arr[3]    стрелка на ящик "arr[3]"

        Незаконно   &(2+2)      тут нет именованного "ящика",
                                на который указывает стрелка,
                                да и вообще ящика нет.

     ИСПОЛЬЗОВАНИЕ УКАЗАТЕЛЕЙ

Указатели несколько различно ведут себя СЛЕВА и СПРАВА
от оператора присваивания.
Нас интересует новая операция, применяемая только к указателям:

        *pointer

  ------------------------------------------------------------------------

СПРАВА от присваиваний и в формулах
===================================
*pointer        означает
                "взять значение переменной (лежащее в ящике),
                на которую указывает указатель,
                хранящийся в переменной pointer".

В нашем примере - это число 12.

То есть *pointer означает "пройти по стрелке и взять указываемое ею ЗНАЧЕНИЕ".

        printf("%d\n", *pointer);

Печатает 12;

        z = *pointer;        /* равноценно z = 12;        */
        z = *pointer + 66;   /* равноценно z = 12 + 66;   */

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

        pointer = &var2;

        ________
       /pointer/
     _/_______/_
     |         |
     |  &var2  |
     |         |
     |_______|_|
             |
             |&var2
             |
             V
        ________
       / var2  /
     _/_______/_
     |         |
     |   43    |
     |         |
     |_________|

После этого   z = *pointer;
означает      z = 43;

  ------------------------------------------------------------------------

Таким образом, конструкция

        z = *pointer;

            означает

        z = *(&var2);

            означает

        z = var2;

То есть * и & взаимно СТИРАЮТСЯ.

     СЛЕВА от присваивания...

        *pointer = 123;

Означает        "положить значение правой части (т.е. 123)
                 в переменную (ящик), на который указывает указатель,
                 хранящийся в переменной pointer".

Пройти по стрелке и положить значение в указываемую переменную.

В данном случае *pointer обозначает
не ЗНАЧЕНИЕ указываемой переменной,
 а САМУ     указываемую переменную.

        ________
       /pointer/
     _/_______/_
     |         |
     |  &var2  |
     |         |
     |_______|_|
             |
             |Положить туда 123
             |
             V
        ________
       / var2  /
     _/_______/_
     |         |
     |   123   |
     |         |
     |_________|

        pointer  = &var2;
        *pointer = 123;

                означает

        *(&var2) = 123;

                означает

        var2 = 123;

То есть снова * и & взаимно СТИРАЮТ друг друга.

  ------------------------------------------------------------------------

Ещё пример:

        *pointer = *pointer + 66;

                или

        *pointer += 66;

  ------------------------------------------------------------------------

Вернемся к примеру с функцией (@). Как он работает?

В строке /* #1 */
        Мы вызываем функцию f(), передавая в нее
        УКАЗАТЕЛЬ на переменную y ("адрес переменной y").

В строке /* #2 */
        Отводится локальная переменная с именем ptr,
        которая в качестве начального значения
        получает значение первого аргумента функции в точке вызова -
        то есть УКАЗАТЕЛЬ на y.

В строке /* #3 */
        Мы видим
                *ptr = 7;

        что следует рассматривать как

                *(&y) = 7;          точнее *(&main::y)=7;

        то есть как

                y = 7;              точнее main::y=7;

        Что и хотелось.

При этом отметим, что само имя "y" этой переменной
внутри функции f() НЕВИДИМО и НЕИЗВЕСТНО!

  ------------------------------------------------------------------------

ПРИМЕР: обмен значений двух переменных.

void main(){
        int x, y;
        int temporary;  /* вспомогательная переменная */

        x=1; y=2;

        temporary=x; x=y; y=temporary;
        printf("x=%d y=%d\n", x, y);    /* Печатает x=2 y=1 */
}
  ------------------------------------------------------------------------

Теперь то же с использованием адресов и указателей:

void swap(int *a, int *b){
        int tmp;

        tmp = *a; *a = *b; *b = tmp;
}

void main(){
        int x, y;

        x = 1; y = 2;
        swap(&x, &y);
        printf("x=%d y=%d\n", x, y);
}
  ------------------------------------------------------------------------

Ещё пример:

        int x;
        int *ptr1, *ptr2;

        ptr1 = &x; ptr2 = &x;
        *ptr1 = 77;
        printf("%d\n", *ptr2);          /* Печатает 77 */

То есть на одну переменную МОГУТ указывать несколько указателей.
  ------------------------------------------------------------------------

Ещё пример:
        int x;
        int *ptr1;              /* Не инициализирована */

        x = *ptr1;

В ptr1 нет указателя ни на что, там есть мусор.
Указатель указывает "в никуда" (пальцем в небо).
Скорее всего произойдёт сбой в работе программы.

Мораль: ВСЕГДА инициализируй переменные, указатели в том числе.

     МАССИВЫ

Язык Си работает с именами массивов специальным образом.
Имя массива "a" для

        int a[5];

является на самом деле указателем на его нулевой элемент.

То есть у нас есть переменные (ящики)
с именами

        a[0]    a[1]    ...     a[4].

При этом само имя a при его использовании в программе означает &a[0]

        a
        |
        |
        |
        V
       a[0]    a[1]    a[2]    a[3]    a[4]
     _________________________________________
     |       |       |       |       |       |
     |       |       |       |       |       |
     |       |       |       |       |       |
     -----------------------------------------

Поэтому

        int a[5];

        /* Передаётся не КОПИЯ самого массива, а копия УКАЗАТЕЛЯ на его начало */

        void f(int *a){         /* или f(int a[]), что есть равноценная запись */
                printf("%d\n", a[1]);
                a[2] = 7;
        }

        main (){
                a[1] = 777;
                f(a);           /* аргумент - массив */
                printf("%d\n", a[2]);
        }

Вызов f(a); сделает именно ожидаемые вещи.
В этом примере мы видим два правила:

ПРАВИЛО_1:
        При передаче в функцию имени массива
        в аргумент функции копируется не весь массив (жирновато будет),
        а указатель на его 0-ой элемент.

ПРАВИЛО_2:
        Указатель на начало массива
        МОЖНО индексировать как сам массив.
        Это вторая операция, помимо *pointer,
        применимая к указателям: pointer[n].

Второе правило влечет за собой ряд следствий.

        int a[5];       /* массив */
        int *ptr;       /* указательная переменная */

        ptr = a;        /* законно, означает ptr = &a[0]; */

Теперь

        ptr[0] = 3;     /* означает a[0] = 3;   */
        ptr[1] = 5;     /* означает a[1] = 5;   */

Более того. Возьмем теперь

        ptr = &a[2];

       a[0]    a[1]    a[2]    a[3]    a[4]
     _________________________________________
     |       |       |       |       |       |
 a:  |       |       |       |       |       |
     |       |       |       |       |       |
     ----------------------------------------------
                     |       |       |       | ...
 ptr:                |       |       |       |
                     -----------------------------
        -2      -1     ptr[0]  ptr[1]  ptr[2]

Мы как бы "приложили" к массиву a[] массив ptr[].

В котором

        ptr[0]  есть    a[2]
        ptr[1]  есть    a[3]
        ptr[2]  есть    a[4]
        ptr[3]          находится за концом массива a[], МУСОР

Более того, допустимы отрицательные индексы!

        ptr[-1] есть    a[1]
        ptr[-2] есть    a[0]
        ptr[-3]         находится перед началом массива a[], МУСОР

Итак: индексировать можно И массивы И указатели.

Кстати, для имени массива a[]
        *a означает то же самое, что и a[0].

Это обратное следствие из схожести массивов и указателей.
     19.c

/* Задача: написать функцию инвертирования порядка символов
   в массиве char.
        A B C D ---> D C B A
   В решении можно использовать рекурсию.
 */

/* Мы приведем рекурсивное и нерекурсивное решения (два варианта) */
#include

/* Сначала - несколько служебных функций. */

/* ФУНКЦИЯ ПОДСЧЕТА ДЛИНЫ СТРОКИ.
   Как уже объяснялось, строка текста - это массив char,
   в конце которого помещен символ '\0'.
   Сам символ \0 не считается.
 */
int strlen(char s[]){                   /* функция от массива букв            */
        int counter = 0;                /* счетчик и одновременно индекс      */

        while(s[counter] != '\0')       /* пока не встретился признак конца текста */
                counter++;              /* посчитать символ                   */
        return counter;                 /* сколько символов, отличных от '\0' */
}

/* ФУНКЦИЯ ПЕЧАТИ СТРОКИ.
   Печатаем каждый элемент массива как символ при помощи putchar(c).
   Как только встречаем элемент массива, равный '\0' - останавливаемся.
   Заметьте, что при наличии завершающего символа нам НЕ НАДО
   передавать в функцию размер массива, он нам неинтересен.

   В конце эта функция переводит строку.
*/
int putstr(char s[]){
        int i = 0;      /* индекс */

        while(s[i] != '\0'){
                putchar(s[i]);
                i++;
        }
        putchar('\n');
        return i;
}

/* ТЕПЕРЬ МЫ ЗАНИМАЕМСЯ ФУНКЦИЕЙ ИНВЕРТИРОВАНИЯ.
   Для этого нам нужна вспомогательная функция:
   сдвиг элементов массива на 1 влево.

        Исходный массив: A B C D E F
                         <----------
        Результат:       B C D E F F
                                   -
        Последний элемент удваивается.

        n - размер массива.
        Функция работает так:

        Исходный массив: A B C D E F  n=6
        После i=1        B B C D E F
        После i=2        B C C D E F
        После i=3        B C D D E F
        После i=4        B C D E E F
        После i=5        B C D E F F
        i=6 ==> остановка.

*/
void shiftLeft(char s[], int n){
        int i;

        for(i=1; i < n; i++)
                s[i-1] = s[i];
}

/* Функция инвертирования.
   Идея такова:
        - если длина массива меньше или равна 1, то инвертировать нечего,
          ибо массив состоит из 1 или 0 элементов.
        - если длина массива > 1, то
          a) Спасти нулевой элемент массива.
                A B C D E F
                |
                |
                V
               tmp

          b) Сдвинуть массив влево
                B C D E F F

          c) В последний элемент массива поместить спасенный нулевой элемент.
                         tmp
                          |
                          V
                B C D E F A

          d) Инвертировать начало массива длиной n-1.
               {B C D E F}A

             Получится:
                F E D C B A

             Что и требовалось.

        s[] - массив,
        n   - его длина.
*/
void reverse(char s[], int n){
        char tmp;

        if(n <= 1)              /* нечего инвертировать */
                return;

        tmp = s[0];             /* спасти */
        shiftLeft(s, n);        /* сдвинуть */
        s[n-1] = tmp;           /* переместить */

        reverse(s, n-1);        /* инвертировать начало */
}

/* ВТОРАЯ ВЕРСИЯ нерекурсивна. Рекурсия заменена циклом.
   Длина начала массива, которую надо инвертировать,
   вынесена на переменную length.
*/
void reverse1(char s[], int n){
        char tmp;
        int length;

        for(length=n; length > 1; --length){
                tmp = s[0];
                shiftLeft(s, length);
                s[length-1] = tmp;
        }
}

char testString[] = "abcdefghijklmnopqrstuvwxyz";

/* Если не задать размер массива, он будет вычислен компилятором автоматически.
   Он будет равен числу букв внутри "..." ПЛЮС одна ячейка для невидимого
   символа '\0' на конце.
   В данном случае это 27.
*/

void main(){
        int len;

        len = strlen(testString);
        /* вычислить длину строки: 26 ('\0' на конце не считается) */

        printf("Строка для теста: \"%s\", ее длина %d\n",
                                    testString,    len);
        /* Обратите внимание на два момента:
                - строку (массив char) следует печатать по формату %s
                - чтобы внутри "..." напечатать символ "
                  надо изобразить его как \"

           А чтобы в putchar напечатать символ ' надо писать putchar('\'');
         */

        /* Первая инверсия */
        reverse(testString, len);
        putstr("Инвертированная строка:");
        putstr(testString);

        /* Вторая инверсия - возвращает в исходное состояние */
        reverse1(testString, len);
        putstr("Инвертированная в исходное состояние строка:");
        putstr(testString);
}
     19_1.c

/* Еще более простой вариант решения:
   просто обменивать элементы местами.

        A B C D E F G H I J
        J B C D E F G H I A
        |                 |     эти
        J B C D E F G H I A
        J I C D E F G H B A
          |             |       потом эти
        J I C D E F G H B A
        J I H D E F G C B A
            |         |         потом эти
          ---->     <-----

        J I H D E F G C B A
        J I H G E F D C B A
              |     |
        J I H G E F D C B A
                | |
        J I H G F E D C B A

                стоп.
*/

#include

/* Обмен значений двух переменных типа char */
void swap(char *s1, char *s2){
        char c;

        c = *s1; *s1 = *s2; *s2 = c;
}

void reverse(char s[], int n){
        int first, last;

        first = 0;              /* индекс первого элемента массива */
        last = n-1;             /* индекс последнего элемента массива */

        while(first < last){    /* пока first левее last */
                swap(&s[first], &s[last]);
                first++;        /* сдвинуть вправо */
                last--;         /* сдвинуть влево  */
        }
}

char testString[] = "abcdefghijklmnopqrstuvwxyz.";

void main(){
        int len;

        len = strlen(testString);       /* Есть такая стандартная функция */
        reverse(testString, len);
        printf("Инвертированная строка: %s\n", testString);
}
     19_2.c

/* Еще один вариант решения:
   сформировать ответ в дополнительном массиве,
   а потом скопировать его на прежнее место.
*/

#include

char testString[] = "abcdefghijklmnopqrstuvwxyz.";

/* Конструкция sizeof(массив)/sizeof(массив[0])
   выдает размер массива, даже если он не был явно объявлен.
   Эта конструкция применяется (чаще всего) для задания массива
   с размером, равным размеру уже объявленного массива.
 */

char tempString[ sizeof(testString) / sizeof(testString[0]) ];

void reverse(char s[], int n){
        int i;

        /* вывернуть, результат в tempString[] */
        for(i=0; i < n; i++)
                tempString[n-1-i] = s[i];

        tempString[n] = '\0';   /* признак конца строки */

        /* скопировать на старое место */
        for(i=0; i < n; i++)
                s[i] = tempString[i];

        s[n] = '\0';            /* признак конца строки */
}

void main(){
        int len;

        len = strlen(testString);       /* Есть такая стандартная функция */
        reverse(testString, len);
        printf("Инвертированная строка: %s\n", testString);
}
     19_3.c

/* Задача инвертирования массива целых чисел */

#include

int arr[]  = {1, 5, 10, 15, 20, 25, 30};
int arrLen = sizeof(arr) / sizeof(arr[0]);   /* размер массива */

/* Распечатка массива в строку */
void printit(int row[], int n){
        int i;

        for(i=0; i < n; i++){
                printf("%d", row[i]);

                if(i == n-1) putchar('\n');
                else         putchar(' ');
        }
}
/* Печать отступа. Отладочная функция */
void printShift(int n){
        n = arrLen - n;
        while(n > 0){
                printf("   ");
                n--;
        }
}
/* Сдвиг массива */
void shiftleft(int row[], int n){
        int i;

        for(i=1; i < n; i++)
                row[i-1] = row[i];
}
/* Инвертирование */
void reverse(int  row[], int n){
        int pocket;

        printShift(n);                          /* трассировка */
        printf("CALLED reverse(row, %d)\n", n); /* трассировка */

        if(n <= 1){
                printShift(n);                  /* трассировка */
                printf("return from reverse(row, %d);\n", n); /* трассировка */
                return;
        }

        pocket = row[0];
        shiftleft(row, n);
        row[n-1] = pocket;

        printShift(n);                          /* трассировка */
        printit(arr, arrLen);                   /* трассировка */

        reverse(row, n-1);

        printShift(n);                          /* трассировка */
        printf("all done; return from reverse(row, %d);\n", n); /* трассировка */
}
void main(){
        reverse(arr, arrLen);
        printit(arr, arrLen);
}

     20.c

/* Задача: написать функцию для распечатки массива целых чисел
   в виде таблицы в columns столбцов.
   При этом порядок элементов должен быть таков:

   0    4    8
   1    5    9
   2    6    10
   3    7
*/

/* Пусть в массиве n элементов.
   Если n < columns, то мы получаем такую ситуацию (n=2, columns=4)

        0       1       пусто       пусто

   Поэтому

        if(n < columns) columns = n;

   Далее, прямоугольная таблица с columns столбцами и lines строками
   может содержать максимум columns*lines элементов. Поэтому:

        columns*lines >= n

   Вычислим число строк.
   Нам нужно минимальное целое число строк, такое что

        lines >= n/columns

   Такое число вычисляется по формуле

        lines = (n + (columns - 1)) / columns;

   где деление целочисленное.

   Далее надо только вывести формулу для индекса элемента в массиве
   в зависимости от номера строки (y) и столбца (x).

   index(x, y) = (x * lines + y);
                 причем если index >= n, то ничего не выводить
*/

#include

int array[100];

void printArray(int a[], int n, int columns){
        int lines;      /* число строк */

        int x, y;       /* номер колонки, номер строки - с нуля */
        int index;      /* индекс элемента в массиве */

        if(n < columns) columns = n;
        lines = (n + (columns-1)) / columns;

        /* Используем вложенные циклы: по строкам, а внутри - по столбцам */
        for(y=0; y < lines; y++){
                for(x=0; x < columns; x++){
                        index = x * lines + y;
                        if(index >= n)  /* элемент за концом массива */
                                break;  /* прервать строку */

                        /* break выводит только
                           из внутреннего цикла (по столбцам) */

                        /* сделать отступ в следующую колонку */
                        if(x != 0) putchar('\t');

                        printf("%02d|%d", index, a[index]);
                        /* Формат %02d заставляет печатать целое число
                           с использованием ДВУХ цифр, причем если
                           число состоит из одной цифры, то спереди
                           приписывается нуль.
                         */
                }
                putchar('\n');  /* перейти к следующей строке */
        }
}
void main(){
        int i, cols;

        /* Инициализация значений элементов массива */
        for(i=0; i < 100; i++)
                array[i] = i + 1;

        for(cols=4; cols <= 13; cols++){
                printf("\t\t* * * ТАБЛИЦА В %d СТОЛБЦОВ * * *\n", cols);
                printArray(array, 77, cols);
                putchar('\n');
        }
}
     20_1.c

#include

main(){
        int x, y;
        int COLUMNS = 11;
        int LINES = 10;

        int value;

        /* цикл по строкам */
        for(y=0; y < LINES; y++){

                /* цикл по столбцам */
                for(x=0; x < COLUMNS; x++){

                        /* что напечатать */
                        value = LINES * x + y;

                        /* если это не нулевой столбец, то перейти
                           в следующую колонку
                         */

                        if(x > 0) putchar('\t');

                        /* ... и в ней напечатать значение */
                        printf("%d", value);

                }
                putchar('\n');  /* новая строка */
        }
}
     20_2.c

/*
        elem(x, y) = LINES * x + y;

                тогда

        elem(0, y+1) - elem(COLUMNS-1, y) = 1 + LINES - COLUMNS*LINES;
        elem(x+1, y) - elem(x, y)         = LINES;
*/

#include

int A = 150;            /* Количество элементов      */
int COLUMNS = 7;        /* Количество столбцов       */
int LINES;              /* Количество строк          */
int value;              /* Значение в текущей клетке */

int OFFSET_NEXT_COLUMN;
int OFFSET_NEXT_LINE;

/* Рисуем строку таблицы */
void line(){
        int col;        /* номер колонки */

        for(col=0; col < COLUMNS; col++){
                if(value >= A)       /* отсутствующий элемент */
                        printf("* ");
                else    printf("%03d ", value);

                /* Увеличение при переходе в соседнюю колонку */
                value += OFFSET_NEXT_COLUMN;    /* 1 */
        }
        /* Перейти к следующей строке */
        putchar('\n');

        /* Увеличение при переходе из конца одной строки к началу следующей.
           Заметим, что к value уже прибавлено OFFSET_NEXT_COLUMN из точки 1,
           поэтому при переходе в начало следующей строки в сумме прибавляется
           OFFSET_NEXT_COLUMN + OFFSET_NEXT_LINE равное
                1 - LINES*COLUMNS + LINES,
           что соответствует формуле.
         */
        value += OFFSET_NEXT_LINE;              /* 2 */
}

int main(){
        int nline;  /* Номер строки */

        LINES = (A + (COLUMNS - 1)) / COLUMNS;

        OFFSET_NEXT_COLUMN = LINES;
        OFFSET_NEXT_LINE = 1 - LINES*COLUMNS;

        for(nline=0; nline < LINES; nline++)
                line();

        /* возврат 0 из main() означает "программа завершена успешно" */
        return 0;
}
     21.c

/* ДВУМЕРНЫЕ МАССИВЫ */

/*
        Двумерный массив представляет собой двумерную
        прямоугольную таблицу из нумерованных переменных.

        Он объявляется так:

                int array[LINES][COLUMNS];

        А индексируется так:

                    array[y][x]

                где 0 <= y <= LINES - 1
                    0 <= x <= COLUMNS - 1

        +-------------+-------------+-------------+------> ось x
        | array[0][0] | array[0][1] | array[0][2] | ...
        +-------------+-------------+-------------+
        | array[1][0] | array[1][1] | array[1][2] | ...
        +-------------+-------------+-------------+
        | array[2][0] | array[2][1] | array[2][2] | ...
        +-------------+-------------+-------------+
        | ...           ...           ...
        V
       ось y

Пока, на данной стадии знания Си,
я рекомендую вам объявлять двумерные массивы как глобальные
и не пытаться передавать их имена в функции как аргументы.
*/

/* Приведем пример, который заводит двумерный массив букв,
   рисует в нем некую геометрическую фигуру,
   и печатает этот массив.

   Здесь мы приводим алгоритм Брезенхема для рисования прямых,
   объяснения КАК он это делает мы опустим. Пардон.
*/

#define LINES   31      /* число строк    */
#define COLUMNS 79      /* число столбцов */

char field[LINES][COLUMNS];

/* В данной программе массив НЕ является параметром,
   мы работаем с ним как с глобальной переменной.
   Функция рисования прямой линии, алгоритм Брезенхема.
 */
void line(int x1, int y1, int x2, int y2, char sym){
        int dx, dy, i1, i2, i, kx, ky;
        int d;      /* "отклонение" */
        int x, y;
        int flag;

        dy = y2 - y1;
        dx = x2 - x1;
        if (dx == 0 && dy == 0){
                field[y1][x1] = sym;    /* единственная точка */
                return;
        }
        kx = 1; /* шаг по x */
        ky = 1; /* шаг по y */

        /* Выбор тактовой оси */
        if( dx < 0 ){ dx = -dx; kx = -1; } /* Y */
        else if(dx == 0)        kx = 0;    /* X */

        if(dy < 0) { dy = -dy; ky = -1; }

        if(dx < dy){ flag = 0; d = dx; dx = dy; dy = d; }
        else         flag = 1;

        i1 = dy + dy; d = i1 - dx; i2 = d - dx;
        x = x1; y = y1;

        for(i=0; i < dx; i++){
                field[y][x] = sym;      /* нарисовать точку */

                if(flag) x += kx; /* шаг по такт. оси   */
                else     y += ky;

                if( d < 0 ) /* горизонтальный шаг      */
                         d += i1;
                else{       /* диагональный шаг        */
                         d += i2;
                         if(flag) y += ky; /* прирост высоты */
                         else     x += kx;
                }
        }
        field[y][x] = sym; /* последняя точка  */
}

int main(){
        int x, y;

        /* Заполнить поле пробелами */
        for(y=0; y < LINES; y++)
                for(x=0; x < COLUMNS; x++)
                        field[y][x] = ' ';

        /* Нарисовать картинку */
        line(0,0,               0,         LINES-1, '*');
        line(0,0,               COLUMNS-1, 0,       '*');
        line(COLUMNS-1, 0,      COLUMNS-1, LINES-1, '*');
        line(0, LINES-1,        COLUMNS-1, LINES-1, '*');

        line(0,0,               COLUMNS-1, LINES-1, '\\');
        line(COLUMNS-1,0,       0,LINES-1,          '/');

        /* Распечатать массив */
        for(y=0; y < LINES; y++){
                for(x=0; x < COLUMNS; x++)
                        putchar(field[y][x]);
                putchar('\n');
        }
        return 0;
}

  

[ Назад ]










helloworld.ru © 2001-2021
Все права защищены