В последней (m-1)-й строке прямоугольной динамической матрицы m ´ n могут содержаться элементы, не принадлежащие исходному одномерному массиву. Поэтому при переборе элементов массива необходима соответствующая проверка.
comp ** A = NULL;
A = (comp **) malloc( m * sizeof(comp *));
// проверка выделения памяти
for(int i=0; i < m; i++)
A[i] = (comp *) malloc(k * sizeof(comp));
// проверка выделения памяти
for( i =0; i < m; i++)
for( int j =0; j < n; j++)
if( (long) i*k+j < T) // работаем только с элементами
// исходного одномерного массива {
A[i][j].re = 0;
A[i][j].im = 0;
}
// работа программы
// освобождение памяти
for( i =0; i < m; i++)
free(A[i]);
free(A);
Для вывода форматированных данных используется двухэтапный алгоритм. Вначале данные записываются в строку или, как иногда говорят, в память. Затем строка выводится на графический экран.
Например выведем вещественное число с двумя знаками после запятой.
charbuf[5];
floatx = 3.1415;
sprintf(buf, “%4.2”, x);
outtextxy(100, 100, buf);
Цифра 4 в форматной строке задает количество байтов в памяти, которые будет занимать выводимая форматированная информация без учета признака конца строки. Прием используется для предотвращения выхода за диапазон массива. Подобная ошибка возникла бы в случае
x = 13.1415.
Организация задержки экрана не зависит от выбора графического или текстового режима, и реализуется следующим образом
if (!getch())
getch();
Данный код корректно обрабатывает два возможных случая. Во-первых, при нажатии обычной клавиши. При этом с клавиатуры в буфер ввода помещается ненулевой ascii-код символа, соответствующего нажатой клавише. Тогда условие !getch() ложно и второй getch() не вызывается.
Во-вторых, при нажатии клавиши с расширенным кодом (например, функциональные клавиши, стрелки) с клавиатуры в буфер ввода поступают два числа: ноль и scan-код этой клавиши. Тогда условие !getch() истинно, вызывается второй getch(), который считывает scan-код и тем самым очищает буфер.
Другим способом задержки экрана является код
while(!getch())
;
Отметим, что стандартным способом задержки (в учебниках и примерах из справок по функциям) является вызов функции
getch();
Однако этот способ некорректно обрабатывает нажатие клавиши с расширенным кодом. В данном случае в буфере останется один непрочитанный символ, который будет считываться при следующем вводе информации.
В следующем фрагменте выходим из цикла по нажатию клавиши Escape
#define ESC 27
while(1){
if(kbhit()){
char c=getch();
if(c == 0)
{
getch();
continue;
}
else if( c == ESC)
break;
}
//работа цикла
}
В графических приложениях ввод текстовой и числовой информации реализуется также в графическом режиме. Для этого нельзя использовать функцию scanf, так как она предполагает текстовый режим.
Предлагается следующий алгоритм для ввода целого положительного числа. Данные считываются с клавиатуры посимвольно, сохраняются в памяти и затем распознаются с помощью функции sscanfформатированного ввода из строки. Строка выводится в графическом режиме посимвольно.
#define ENTER 13
main(){
//инициализация графического режима
char c, buf[2], *str=(char *)malloc(1);
int number;
str[0] = ’\0’;
buf[1] = ‘\0’;
while( (c = getch()) != ENTER){
if( c < ’0’ || c> ’9’)
continue;
buf[0] = c; // введенный символ оформляем в виде строки
outtext(buf); // и выводим на экран в графическом режиме
str = realloc(str, strlen(str) + 2);
str[strlen(str) + 1] = ‘\0’;
str[strlen(str)] = c; // запоминаемсимволвпамяти
}
sscanf(str, “%d”, &number);
}
Проверку выхода переменной x из области допустимых значений функции f(x) можно реализовать в самом теле функции. В случае выхода из ОДЗ функция взводит глобальный флаг, который проверяется в вызывающей функции.
Например,
intflag=0; //глобальный флаг
// работаем с функцией y = x1/2
float f(float x){
if(x<0){
flag=1;
return 0;
}
else{
flag=0;
return sqrt(x);
}
}
void main(){
float x, y;
scanf(“%f”, &x);
y=f(x);
if(flag==1)
printf(“Выход из ОДЗ. ”);
else
printf(“Нет выхода из ОДЗ. Продолжаем вычисления”);
}
Для рисования графика функции программисту удобнее использовать математическую систему координат (МСК), расположенную по центру экрана. Графические функции работают в графической системе координат (ГСК).
а б
Рис. 1. Системы координат: а – математическая; б – графическая
Инициализируем переменные.
intmaxx = getmaxx(),
maxy = getmaxy(),
px = 30, // количество пикселей в одной математической единице по оси Ох
py = px * ((float)maxx/maxy); // количество пикселей в одной математической единице по оси Оy.
Соответствие между двумя системами координат представлено
ниже
Соответствие между МСК и ГСК
МСК | ГСК |
(0, 0) ………………………………. | (maxx / 2, maxy / 2) |
(1, 0) ………………………………. | (maxx / 2 + px, maxy / 2) |
(0, 1) ………………………………. | (maxx / 2, maxy / 2 – py) |
(x, y) ….……………………………. | (maxx / 2 + x px, maxy / 2 – y py) |
(x, f(x)) ……………………………. | (maxx / 2 + x px, maxy / 2 – f(x) py) |
Реализуем масштабирование по оси координат Ох так, чтобы график функции y = f(x) на заданном отрезке [a, b] размещался по всей ширине экрана. Для этого найдем коэффициенты u и v линейного отображения g(x) = u x + v, при котором отрезок [a, b] переходит в отрезок [0, maxx]. Из системы линейных уравнений
u a + v = 0
u b + v = maxx
находимu = maxx / (b-a), v = -a maxx / (b-a). Тогда точке (x, f(x)) будет соответствовать пиксель
( (maxx / (b-a)) * x - a * maxx / (b-a) , maxy / 2 - f(x) * py)),
где px = maxx/(b-a). Затем график можно нарисовать с помощью цикла, в котором счетчик является вещественной математической переменной. Например:
for(float x = a; x <= b; x += (b-a) / maxx)
putpixel((maxx / (b-a)) * x - a * maxx / (b-a), maxy / 2- f(x) * py));
Данный цикл при больших a и b может оказаться бесконечным. Это возможно в случае, если шаг цикла (b-a) / maxx будет меньше расстояния между числом a и его ближайшим соседом справа для типа float.
Для рисования вращающейся звезды (см. задание 3.4) лучше использовать две видеостраницы.
int page=0;
for(double f = 0; f < 2 ×M_PI-M_PI / 100; f += M_PI / 50) {
otrisovka(X0, Y0, R1, r2, fi0 + f, COLOR); /* рисуем новую звезду на активной странице, которая по умолчанию имеет нулевой номер*/
setvisualpage(page); /* показываем изображение новой звезды*/
page=abs(page-1); /* меняем номер страницы с 0 на 1 или наоборот*/
setactivepage(page); /* меняем активную страницу*/
otrisovka(X0, Y0, R1, r2, fi0 + f-M_PI/ 50, getbkcolor()); /* стираем старую звезду на активной странице*/
}
Для создания фона в задаче о снегопаде можно использовать 16-ти цветный bmp-файл, так как это устраняет проблему самостоятельного рисования фонового изображения средствами языка C++. Для загрузки изображения из файла надо выполнить действия:
1) с позиции 22 в файле прочитать высоту рисунка;
2) вычислить ширину записанного изображения
ширина = (размер_файла - 118) / высота;
3) загрузить сам рисунок, начиная с позиции 118, учитывая, что в одном байте содержится 2 пикселя и то, что изображение в файле записано построчно, причем первая строка записана в конец файла, а последняя – с позиции 118.
// карта замещения цветов для создания визуального эффекта
char map[] = {0,12,2,6,9,5,3,8,7,4,10,14,1,13,11,15};
int y0 = getmaxy();
// Открываем картинку
FILE *f = fopen(fon, "rb");
if(f==NULL)
return 2;
// читаем ширину картинки
fseek(f, 0, 2);
long l = ftell(f)-118;
fseek(f, 22, 0);
int w,h;
fread(&h, 2, 1, f);
w = int(l / h);
// читаем и рисуем картинку
fseek(f, 118, 0);
int x=0;
int y=0;
while(1) {
c = fgetc(f);
if(feof(f))
break;
ch = map[c/16];
cl = map[c%16];
putpixel(2*x+0, y0-y, ch);
putpixel(2*x+1, y0-y, cl);
if(++x==w) {
x=0;
y++;
}
}
fclose(f);
Вызовы BIOS используют программные прерывания. BIOS имеет несколько различных прерываний для разных целей. Для работы с мышью используют прерывание 0x33. Для доступа к этим прерываниям используется функция Си с прототипом в файле <dos.h>
int int86(int num, REGS *in, REGS *out);
где num – номерпрерывания. Объединение REGS имеетвид
union REGS{
struct WORDREGS x;
struct BYTEREGS y;
};
struct WORDREGS{
unsigned int ax, bx, cx, dx, si, di, cflags, flags;
};
struct BYTEREGS{
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
};
//Определим глобальную переменную
REGSregs;
//показать курсор
void showcursor(void){
regs.x.ax = 0x01;
int86(0x33,®s,®s);
}
//спрятатькурсор
void hidecursor(void){
regs.x.ax = 0x02;
int86(0x33,®s,®s);
}
//получениестатусамыши
void get_mouse_status(int& button,int& x,int& y){
regs.x.ax = 0x03;
int86(0x33,®s,®s);
button = regs.h.bl;
x = regs.x.cx;
y = regs.x.dx;
}
//пример использования мыши
main(){
//инициализация графического режима
int button, x, y;
char str[20];
showcursor();
while(1){
get_mouse_status(button, x, y);
if(x == 0 || y == 0)
break;
sprintf(str, “%d %d”,x, y);
outtext(30, 30, str);
}
hidecursor();
}
На экране в непрерывном режиме рисуются звезды (пиксели) в случайном месте и случайным цветом. Распределение случайных величин равномерное. При наложении новой звезды на другую видимую звезду обе стираются. Рисование прекращается нажатием клавиши Escape. Затем происходит подсчет числа видимых звезд, и процент заполнения неба выводится по центру графического экрана с точностью до сотых долей процента. Использовать готический шрифт размером 1 см.
Рисование звезд и их подсчет реализовать в виде отдельных функций. Получить ответ для двух режимов VGAHI и VGALO.
С верхней части и с боковых сторон экрана в непрерывном режиме падают белые снежинки в форме одного пикселя. Выход из программы по нажатию клавиши Escape. Снежинки пролетают экран и снова появляются. Создать на экране графический фон в виде голубого полумесяца, красивого текста и пр. Фон не содержит белого цвета и не должен перерисовываться.