Основные процедуры управления экраном в текстовом режиме – очистка экрана, опрос/установка позиции курсора, создание окна вывода, изменение цветовых атрибутов.
Управление дисплеем в текстовом режиме
При запуске приложения MS-DOS или консольных приложений дисплей работает в текстовом режиме (в Windows такой режим эмулируется), для которого характерно разделение экрана на строки и столбцы (обычно строк 25, а столбцов 80, но существуют и другие текстовые режимы). На каждом пересечении строки и столбца находится условный прямоугольник – знакоместо, в котором размещается отображаемый символ. На экране выделяется текущее знакоместо, помеченное мерцающим курсором (мигающей черточкой или прямоугольником). Именно в этой позиции появляется символ, набираемый пользователем на клавиатуре, или символ, отображаемый программой.
Системы программирования на базе языка C, предоставляют пользователю некоторый набор возможностей по размещению информации на площади экрана и по управлению цветовыми атрибутами отображаемых символов.
Очистку экрана выполняет функция clrscr(), ее название образовано от сокращения английских слов clear (очистить) и screen (экран).
Для перемещения курсора в заданную позицию экрана используется функция
gotoxy(x,y). Ее аргументы определяют номер столбца x (0£x<80) и строки y (0£y<25) того знакоместа, которое станет текущим.
Функция window(x1,y1,x2,y2) позволяет выделить на экране прямоугольную область, в пределах которой будет происходить вывод. По умолчанию областью вывода считается весь экран, его левому верхнему углу соответствуют нулевой столбец и нулевая строка, а правому нижнему углу – 79-й столбец и 24-я строка. Иногда бывает полезно сузить область вывода с тем, чтобы в оставшейся части экрана расположить какую-то заготовку (формуляр бланка, например, или другую пояснительную информацию, не затираемую выводимыми данными и не смещающуюся при выводе). Знакоместо с координатами (x1,y1) задает положение левого верхнего угла окна вывода, а знакоместо с координатами (x2,y2) – положение правого нижнего угла окна вывода.
Так как почти все современные дисплеи цветные, то текстовый режим предоставляет возможность раскрасить каждый выводимый символ. При этом имеется возможность автономно задать цвет фона знакоместа и цвет контура отображаемого символа. Дело в том, что в текстовом режиме для каждого символа, отображаемого на экране, выделено 2 информационных байта. В первом байте располагается код ASCII символа, а во втором хранятся его цветовые атрибуты (рис. 5.1)
Рис. 5.1. Цветовые атрибуты
Цвет формируется в результате наложения трех цветовых компонент – красного (R), зеленого (G) и синего (B). Цвет символа может иметь повышенную яркость (признак J=1). Для привлечения внимания к сообщению на экране символ можно заставить мерцать (признак M=1).
Для установки цвета символов во всех сообщениях, выдаваемых с помощью функции cprintf, предназначена функция textcolor(fc). Ее аргумент должен принадлежать диапазону [0, 15]. Цвет фона для последующего вывода по cprintf устанавливается с помощью функции textbackgrount(bc). Имеется возможность произвести установку цветовых атрибутов за один раз, обратившись к функции textattr. Ее единственным аргументом может быть одно из двух следующих выражений:
(bc<<4)+fc //цвет фона и цвет символов
128+(bc<<4)+fc //мерцание, цвет фона и цвет символов
В качестве примера вывода разноцветного текста приведем следующую программу:
#include <conio.h>
void main()
{ int j;
textbackground(0);
clrscr();
for(j=0; j<24; j++)
{ gotoxy(2*j+1,j+1);
textcolor(128+j);
textbackground(j+2);
cprintf("Color palette");
}
getch();
}
Прямая и косвенная рекурсия при вызове функций
Рекурсивные функции
Рекурсивные определения и рекуррентные вычислительные схемы довольно часто используются в математике. Например:
n! = n*(n-1)! //рекурсивное определение факториала
yn+1=0.5*(yn+x/yn) //итерационная формула метода Ньютона
По аналогии с такими подходами в программировании появились рекурсивные функции, которые вызывают сами себя. Кроме такой непосредственной (прямой) рекурсии возможна и косвенная рекурсия. Она имеет место, когда в цепочке функций, последовательно вызывающих друг друга, последняя функция обращается к первой. Чтобы избежать бесконечного зацикливания, в таких рекурсивных цепочках должно быть обязательно предусмотрено условие выхода.
Наиболее часто цитируемым примером рекурсивной программы является вычисление факториала:
long fact(int n)
{ if (n<2) return 1L;
return (n*fact(n-1));
}
Еще один пример, демонстрирующий вычисление n-го элемента в последовательности чисел Фибоначчи:
Fn=Fn-2+Fn-1
int Fib(int n)
{ if(n<3) return 1;
return Fib(n-2)+Fib(n-1);
}
Пример с числами Фибоначчи представляет собой крайне неэффективную по производительности программу. Дело в том, что каждый вызов функции связан с выделением участка стека, где должны храниться локальные переменные и промежуточные результаты вычислений. А функция Fib содержит в своем теле два вызова самой себя. В большинстве случаев вместо рекурсивной функции не так уж и сложно написать функцию, не содержащую рекурсии, которая и работает быстрее, и более экономно использует оперативную память. Два выше приведенных примера легко поддаются такой переделке:
long fact(int n)
{ long f=1;
for(int j=2; j<=n; j++) f=f*j;
return f;
}
//-------------------------------
int Fib(int n)
{ int j,f,f1=1,f2=1;
if(n<3) return 1;
for(j=3; j<=n; j++)
{ f=f1+f2; f1=f2; f2=f; }
return f;
}
Однако некоторые рекурсивные программы смотрятся очень неплохо. Например, функция нахождения наибольшего общего делителя двух чисел по алгоритму Евклида. Идея ее построения базируется на трех очевидных фактах:
1. НОД(n1,n2)=НОД(n2,n1)
2. НОД(0,n2)=n2
3. НОД(n1,n2)=НОД(n2,n3), где n3=n1(mod n2)
//Рекурсивный вариант нахождения НОД
int nod(int n1,int n2)
{ if(n1==0) return n2;
return nod(n2%n1,n1);
}
Эту функцию тоже несложно преобразовать в программу без рекурсии:
int nod(int n1,int n2)
{ int t;
m: if(n2<n1) {t=n1; n1=n2; n2=t; }
if(n1==0) return n2;
n2=n1%n2;
goto m;
}
Билет 16а