11. Указатели

 Занятие 11


Это надо знать

На прошлом занятии мы рассмотрели движение одного шарика (героя) по траектории. Рассмотрим случай, когда на экране два шарика.

Для первого шарика начальные значения будут следующими:

double   x1 = 100,  double   y1 = 100;
double vx1 =     5,  double vy1 =     7;

Для второго шарика начальные значения будут следующими:

double   x2 = 300,  double   y2 = 200;
double vx2 =     4,  double vy2 =    3;

Нарисуем синий и красный шарики:

        txSetFillColor (TX_RED);
        txCircle (x1, y1, 15);
        
        txSetFillColor (TX_BLUE);
        txCircle (x2, y2, 15);

Функция MoveBall будет содержать команды для движения шарика с координатами (x1, y1) и команды для движения шарика с координатами (x2, y2).

      x1 = x1 + vx1*dt; // изменение координат первого шарика
        y1 = y1 + vy1*dt;

        x2 = x2 + vx2*dt;   // изменение координат второго шарика
        y2 = y2 + vy2*dt;

        if (x1 > 600 - 15)    // проверка столкновения со стенами  первого шарика
            {
            vx1 = -vx1;
            x1  = 600 - 15;
            }
        if (x1 < 0 + 15)
            {
            vx1 = -vx1;
            x1  = 0 + 15;
            }
        if (y1 < 0 + 15)
            {
            vy1 = -vy1;
            y1  = 0 + 15;
            }
        if (y1 > 400 - 15)
            {
            vy1 = -vy1;
            y1  = 400 - 15;
            }

        if (x2 > 600 - 15)   // проверка столкновения со стенами  второго шарика
            {
            vx2 = -vx2;
            x2  = 600 - 15;
            }
        if (x2 < 0 + 15)
            {
            vx2 = -vx2;
            x2  = 0 + 15;
            }
        if (y2 < 0 + 15)
            {
            vy2 = -vy2;
            y2  = 0 + 15;
            }
        if (y2 > 400 - 15)
            {
            vy2 = -vy2;
            y2  = 400 - 15;
            }



Создадим две функции:

Рисование шарика: DrawBall:
Движение шарика: Physics.

Вызов функции DrawBall:

DrawBall (x1, y1, TX_RED);
DrawBall (x2, y2, TX_BLUE); 

Функция DrawBall:

void DrawBall (double x, double y, COLORREF color)
    {
    txSetFillColor (color);
    txCircle (x, y, 15);
    }

Вызов функции Physics.:

Physics (x1, y1, vx1, vy1, dt);
Physics (x2, y2, vx2, vy2, dt);

Функция Physics.:

void Physics (double x, double y, double vx, double vy, double dt)
    {
    x = x + vx*dt;
    y = y + vy*dt;

    if (x > 600 - 15)
        {
        vx = -vx;
        x  = 600 - 15;
        }

    if (x < 0 + 15)
        {
        vx = -vx;
        x  = 0 + 15;
        }

    if (y < 0 + 15)
        {
        vy = -vy;
        y  = 0 + 15;
        }

    if (y > 400 - 15)
        {
        vy = -vy;
        y  = 400 - 15;
        }
    }

Запустив программу на выполнение, увидим, что шарики не двигаются, а стоят на месте в первоначальных координатах (100, 100) и (300, 200).

Почему же так произошло? Рассмотрим по шагам работу программы:
1. Присваиваем значения переменным:   x1 = 100,   y1 = 100,   vx1 =  5,  vy1 =  7.
2. Рисуем круг с центром в точке (100, 100) и радиусом 15.
3. Вызываем функцию Physics, передаем значения параметров:  х = 100, у = 100, vx = 5, vy = 7, dt = 1.
4. Функция Physics изменяет значения х = 100 + 5*1 = 105  и  у = 100 + 7*1 = 107.
5. Возвращаемся в функцию MoveBall, и функция Physics не передала нам новые значения х1 и у1, следовательно, они не меняются, и x1 = 100,   y1 = 100. Шарик стоит на месте.

Указатели

Указатель - специальный тип переменной, который может хранить адрес значения.

Когда мы передаем в функцию Physics значения х1 и у1, функция работает с копиями этих переменных, следовательно, надо изменять не копии, а функция должна знать, где находятся оригиналы переменных и изменять их. Т.е. функция Physics должна принимать не копию переменной х1, а адрес (указатель) того места, где хранится оригинал х1.

В языке Си есть оператор & (амперсант), который записывается перед именем переменной, и такая запись будет означать адрес этой переменной.

Т.к. функция Physics изменяет значения переменных х1, у1, vx1, vу1 для первого шарика и  х2, у2, vx2, vу2 для второго шарика, будем передавать их адреса:

Physics (&x1,  &y1,  &vx1,  &vy1,  dt);
Physics (&x2,  &y2,  &vx2,  &vy2,  dt);

В самой функции Physics теперь надо указать, что х, у, vx, vу - это не числа, а адреса чисел.

Запись double*   x  означает, что х - адрес некоторого числа.

void Physics (double*  x,  double*  y,  double*  vx,  double*  vy,  double dt)

Все строчки, которые содержали переменные х, у, vx, vу, теперь будут работать с адресами этих переменных, поэтому заменим и их на *х,  *у,  *vx,  *vу:
    {
    *x = *x + *vx*dt;
    *y = *y + *vy*dt;

    if (*x > 600 - 15)
        {
        *vx = - *vx;
        *x   = 600 - 15;
        }

    if (*x < 0 + 15)
        {
        *vx = - *vx;
        *x   = 0 + 15;
        }

    if (*y < 0 + 15)
        {
        *vy = - *vy;
        *y   = 0 + 15;
        }

    if (*y > 400 - 15)
        {
        *vy = - *vy;
        *y   = 400 - 15;
        }
    }

Т.к. теперь происходит изменение переменных х1, у1, vx1, vу1 и х2, у2, vx2, vу2, оба шарика будут двигаться.

Пример

Создадим движение нескольких героев по траектории.

Посмотреть текст программы

Результат работы программы

Домашнее задание

Написать программу, которая реализует движение по траектории нескольких героев из библиотеки (использовать указатели).