Список заданий и контрольные сроки сдачи
Лабораторные занятия проводятся в дисплейных классах. Всего лабораторных работ — 7 (описание и сроки приведены ниже). Каждая работа засчитывается при удовлетворении всем требованиям протокола оценки и может быть оценена в зависимости от срока защиты. За каждую неделю задержки базовая оценка за работу уменьшается вдвое. Для получения зачета должны быть сданы все работы, количество набранных по результатам защиты работ баллов участвует в общей экзаменационной оценке (40%).
Сдача работы включает в себя:
- предоставление файла с исходным кодом программы и сопутствующих файлов (Makefile и т. п.)
- презентацию исходного кода (демонстрация, пояснение, ответы на вопросы преподавателя)
- презентацию программы (демонстрация сборки и исполнения программы, ответы на вопросы преподавателя)
- предоставление других документов, указанных в условии задачи
Работающая программа является необходимым, но недостаточным условием сдачи лабораторной работы. Неспособность студента логически объяснить свое решение ведет к отрицательной оценке второго пункта протокола.
Целью выполнения лабораторных работ является получение студентом представлений о написании качественного кода на языке Си.
Задача 1. Калькулятор возраста
Базовая оценка: 4
Срок сдачи: 27.09
И шаблон Makefile (Makefile):/** * hello.c -- программа "Hello, world" * * Copyright (c) 2009, Student Name <student@cs.karelia.ru> * * This code is licensed under a MIT-style license. */ #include <stdio.h> int main() { /* Текущий год */ int year; /* Запрашиваем с клавиатуры текущий год */ fprintf(stdout, "Введите который сейчас год: "); fscanf(stdin, "%d", &year); /* Выводим приветствие и пожелание на следующий год */ fprintf(stdout, "Hello, students!\nУдачи в %d году\n", year + 1); return 0; }
# цель по умолчанию (при вызове make или make hello) # собираем программу hello из объектного файла hello.o hello: hello.o gcc -g -O0 -o hello hello.o hello.o: hello.c gcc -g -O0 -c hello.c # цель clean (при вызове make clean) # удаляем программу и объектные файлы clean: rm hello *.o
Необходимо выполнить следующие действия вместе с инструктором:
- Подготовить каталог для программы
- Скопировать код программы в каталог
- Выполнить сборку и запуск вручную
- Выполнить сборку посредством Makefile (отдельно и из emacs)
- Внести намеренную ошибку, разобрать диагностическое сообщение, обратить внимание на номер строки с ошибкой
- Выполнить программу в отладчике:
- выполнить по шагам, отслеживая значение year
- поставить контрольную точку на последний fprintf
- обратить внимание на неинициализированную переменную year
Самостоятельно модифицировать программу следующим образом: пользователь вводит текущий год и год рождения, необходимо рассчитать и вывести на экран сколько лет исполняется пользователю в этом году.
Задача 2. Баллистическая траектория
Базовая оценка: 4
Срок сдачи: 11.10
Предлагается шаблон программы расчета координат снаряда, выпущенного из точки (0, 0) с начальной скорость v0 под углом theta к горизонту (bullet.c):
/** * bullet.c -- программа расчета координат снаряда * * Программа расчета координат снаряда, выпущенного из точки (0, 0) * с начальной скорость v0 под углом theta к горизонту. * * Copyright (c) 2009, Student Name <student@cs.karelia.ru> * * This code is licensed under a MIT-style license. */ #include <stdio.h> #include <math.h> #define GRAVITY_ACCELERATION 9.8 int main() { double velocity, angle, t; double x, y; /* Ввод начальных данных */ fprintf(stdout, "Введите начальную скорость в м/c: "); fscanf(stdin, "%lf", &velocity); fprintf(stdout, "Введите угол броска в радианах: "); fscanf(stdin, "%lf", &angle); fprintf(stdout, "Введите длительность полета в c: "); fscanf(stdin, "%lf", &t); /* Рассчет координат */ x = velocity * cos(angle) * t; y = velocity * sin(angle) * t - GRAVITY_ACCELERATION * t * t / 2; /* Вывод результата */ fprintf(stdout, "Снаряд в точке %.2lf %.2lf\n", x, y); return 0; }
Необходимо модифицировать программу таким образом, чтобы дополнительно запрашивался интервал времени и шаг, выводилась таблица, содержащая поля «момент времени», «координата x», «координата y», «в полете» (да или нет).
Задача 3. Постоянная Капрекара
Базовая оценка: 8
Срок сдачи: 25.10
Реализовать программу, позволяющую на тестовых примерах проверить существование постоянной Капрекара, то есть числа, к которому сходится следующий алгоритм:
- Возьмем произвольное n-значное число, в котором не все цифры равны
- Получим новое число, сортировкой цифр по убыванию
- Получим второе новое число, сортировкой цифр по возрастанию
- Вычтя из первого числа второе, получим новое
- Если результат не равен исходному числу, перейти к п.2
Капрекар установил, что для n = 4, существует постоянная 6174, также известно, что для n = 3, постоянная равна 495.
В программе должна быть предусмотрена возможность отсутствия требуемой константы.
Дан следующий шаблон (kaprekar.c):
/** * kaprekar.c -- проверка существования постоянной Капрекара * * Программу, позволяющая на тестовых примерах проверить существование постоянной Капрекара, * то есть числа, к которому сходится следующий алгоритм: * - Возьмем произвольное n-значное число, в котором не все цифры равны * - Получим новое число, сортировкой цифр по убыванию * - Получим второе новое число, сортировкой цифр по возрастанию * - Вычтя из первого числа второе, получим новое * - Если результат не равен исходному числу, перейти к п.2 * * Copyright (c) 2009, Student Name <student@cs.karelia.ru> * * This code is licensed under a MIT-style license. */ #include <stdio.h> #include <stdlib.h> #define NUM_LENGTH 4 int main(int argc, char** argv) { int digit_sum[NUM_LENGTH]; /* цифры текущего числа */ int digit_min[NUM_LENGTH]; /* цифры числа по возрастанию */ int digit_max[NUM_LENGTH]; /* цифры числа по убыванию */ int i = 0; /* счетчик */ int num = 0; /* вводимое число */ /* чтение ввода до тех пор, пока не будет правильное число */ fprintf(stdout, "Введите число %i знаков: ", NUM_LENGTH); fscanf(stdin, "%i", &num); /* Преобразование числа в массив */ for (i = 0; i < NUM_LENGTH; i++) { digit_sum[i] = num - (num / 10) * 10; num = num / 10; } /* Обратное преобразование */ num = 0; for (i = 0; i < NUM_LENGTH; i++) { num = num * 10 + digit_sum[NUM_LENGTH - i - 1]; } fprintf(stdout, "Результат равен %d\n", num); return EXIT_SUCCESS; }
Задача 4. Контроллер информационного табло
Базовая оценка: 4
Срок сдачи: 8.11
Имеется устройство, представляющее собой табло 80x24, каждая ячейка которого задается двумя целочисленными координатами (левый верхний угол — 0,0) и может находится в двух состояниях (включено/выключено, 1/0). Контроллер табло понимает ряд управляющих команд в кодах:
- команда 0 — вывод табло
- команда 1 — переключение ячейки с заданными координатами x y
- команда 2 — запомнить состояние заданной ячейки с координатами x y
- команда 3 — переключение ячейки с заданными координатами x y, если запомненное состояние равно 1
Дана программа формирования изображения на экране табло, представленная в следующем формате: каждая строка программы состоит из кода команды и параметров (если необходимо) через пробел. В начале работы все ячейки табло находятся в состоянии 0.
Необходимо написать программу, получающую на входе файл с программой в кодах и выводящую в стандартный вывод последовательность экранов табло (для каждой команды вывода), изображая включенные ячейки символом '*', выключенные — символом '.'.
Имя файла с программой в кодах передается аргументом командной строки, если опущен — подразумевается стандартный ввод.
Студентам предложен шаблон кода считывания команды и анализ с помощью оператора switch (board_controller.c):
Пример ввода:/** * board_controller.c -- Контроллер информационного табло. * * Copyright (c) 2009, Student Name <student@cs.karelia.ru> * * This code is licensed under a MIT-style license. */ #include <stdio.h> #include <stdlib.h> /* Ширина табло. */ #define BOARD_WIDTH 80 /* Высота табло. */ #define BOARD_HEIGHT 24 /* Инструкции. */ #define INSTR_SHOW 0 #define INSTR_FLIP 1 #define INSTR_GET 2 #define INSTR_FLIPIF 3 /* Состояние табло. */ int board[BOARD_WIDTH][BOARD_HEIGHT] = { { 0 } }; int main(int argc, char** argv) { /* Инструкция. */ int ins; /* Результат fscanf (количество прочитанных значений или EOF). */ int read; /* Поток ввода. */ FILE* input = stdin; /* TODO: ввод из файла. */ /* Считывать команды пока не конец файла */ while ((read = fscanf(input, "%d", &ins)) != EOF) { if (read != 1) { /* Ошибка разбора входных данных. */ fprintf(stderr, "Неверный формат входных данных\n"); return EXIT_FAILURE; } switch (ins) { case INSTR_SHOW: /* TODO: реализация SHOW. */ break; case INSTR_FLIP: /* TODO: реализация FLIP. */ break; case INSTR_GET: /* TODO: реализация GET. */ break; case INSTR_FLIPIF: /* TODO: реализация FLIPIF. */ break; default: fprintf(stderr, "Неверная инструкция: %d\n", ins); return EXIT_FAILURE; } } return EXIT_SUCCESS; }
1 0 0 1 1 1 1 2 2 2 0 0 3 1 0 2 2 2 3 1 2 0Результат:
**.............................................................................. .*.............................................................................. .**............................................................................. ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................
Задача 5. Информационное табло
Базовая оценка: 8
Срок сдачи: 22.11
На вход подается текст программы на высокоуровневом языке, описанном ниже. На выходе необходимо получить команды, которые могут быть обработаны программой из задачи 4.
Синтаксис высокоуровневого языка:
- SHOW - вывод табло
- FLIP X Y - переключение ячейки с заданными координатами X Y
- GET X Y - запомнить состояние заданной ячейки с координатами X Y
- FLIPIF X Y - переключение ячейки с заданными координатами X Y, если запомненное состояние равно 1
- CLEAR - очистка табло (для каждой ячейки табло выполнить GET и FLIPIF)
- FLIPRECT L, T, W, H - переключение ячеек области (выполнить FLIP для каждой точки прямоугольника заданного параметрами)
L, T - координаты левой верхней точки
W - ширина прямоугольника
H - высота прямоугольника
Вход:
FLIPRECT 30 6 10 7 FLIPRECT 35 8 7 10 FLIP 41 16 FLIP 40 16 FLIP 39 16 FLIP 41 12 FLIP 40 7 SHOWВыход:
1 30 6 1 30 7 1 30 8 1 30 9 1 30 10 1 30 11 ... (124 строки вырезано) 1 41 16 1 40 16 1 39 16 1 41 12 1 40 7 0При перенаправлении вывода данной программы на вход программы из задачи 4 можно получить изображение на табло:
$ ./pr5 input | ./pr4 ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ..............................**********........................................ ..............................***********....................................... ..............................*****.....**...................................... ..............................*****.....**...................................... ..............................*****.....**...................................... ..............................*****.....**...................................... ...................................******....................................... ...................................*******...................................... ...................................*******...................................... ...................................*******...................................... ...................................****......................................... ...................................*******...................................... ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................
Задача 6. Информационное табло. Версия 2
Базовая оценка: 8
Срок сдачи: 6.12
Условие задачи 5. Дополнительно реализовать специальные эффекты:
- HMIRROR — правая половина — зеркало левой
- VMIRROR — нижняя половина — зеркало верхней
- SETROW — зажечь заданную строку табло
- SETCOL — зажечь заданный столбец табло
Каждому студенту придумать еще один эффект и реализовать. Каждый эффект должен быть реализован в виде отдельной функции.
Даны прототипы и код некоторых функций, которые рекомендуется использовать:
/* * Отразить левую половину табло на правую. */ void hmirror() { /* TODO: реализовать команду HMIRROR */ } /* * Отразить верхнюю половину табло на нижнюю. */ void vmirror() { /* TODO: реализовать команду VMIRROR */ } /* * Зажечь заданную строку. * row - номер строки. */ void setrow(int row) { /* TODO: реализовать команду SETROW */ } /* * Зажечь заданный столбец. * col - номер столбца. */ void setcol(int col) { /* TODO: реализовать команду SETCOL */ } /* * Очистить (погасить) прямоугольник. * x, y - координаты левого верхнего угла, * w, h - ширина и высота. */ void clear_rect(int x, int y, int w, int h) { /* TODO: реализовать очистку заданного прямоугольника */ } /* * Чтение и проверка значений координат. * * input - поток ввода. * x - указатель, по которому будет записано значение первой координаты. * y - указатель, по которому будет записано значение второй координаты. * * Возвращаемое значение: * 1 - координаты считаны успешно * 0 - произошла ошибка при обработке ввода */ int read_location(FILE* input, int* x, int* y) { int read; /* Чтение координат. */ read = fscanf(input, "%d %d", x, y); if (read != 2) { /* Непредвиденный конец ввода или ошибка разбора входных данных. */ fprintf(stderr, "Неверный формат входных данных\n"); return 0; } if (*x < 0 || *x >= BOARD_WIDTH || *y < 0 || *y >= BOARD_HEIGHT) { fprintf(stderr, "Координаты за пределами табло\n"); return 0; } return 1; }
Задача 7. Календарь
Базовая оценка: 4
Срок сдачи: 20.12
Написать программу, выводящую календарь на текущий месяц в таком же формате, каком это делает команда cal.
По умолчанию началом недели считается воскресенье, при указании ключа -m неделя должна начинаться с понедельника.
Дан шаблон (calendar.c):/** * calendar.c -- вывод календаря на текущий месяц * * Программа, выводящая календарь на текущий месяц в таком же формате, в * каком это делает команда cal. * * По умолчанию началом недели считается воскресенье, при указании ключа -m * неделя должна начинаться с понедельника. * * Copyright (c) 2009, Student Name <student@cs.karelia.ru> * * This code is licensed under a MIT-style license. */ #include <time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> /* Количество дней в неделе */ #define WEEKDAYS 7 /* Максимальное количество недель в месяце */ #define MAX_WEEKS 6 /* Данная функция заполняет переданный двумерный массив таким образом, чтобы каждая строка соответствовала * одной неделе заданного месяца, а каждый столбец - дню недели (понедельник, вторник, среда и т.д.). * Дни, которым в заданном месяце не соответствует ни одно число заполняются нулями. Пример * заполнения массива для сентября 2009 года, начало недели - понедельник: * { * { 0, 1, 2, 3, 4, 5, 6}, * { 7, 8, 9, 10, 11, 12, 13}, * {14, 15, 16, 17, 18, 19, 20}, * {21, 22, 23, 24, 25, 26, 27}, * {28, 29, 30, 0, 0, 0, 0} * } * * month - месяц, * year - год, * cal - заполняемый массив, * week_start - с какого дня начинается неделя (0 - воскресенье, 1 - понедельник, и т.д.) */ int get_month_matrix(unsigned int month, unsigned int year, int cal[][WEEKDAYS], unsigned int week_start); int main(int argc, char **argv) { /* Код программы */ return EXIT_SUCCESS; }