(c) Larry Ewing, Simon Budig, Garrett LeSage
с 1994 г.

Кафедра Информатики и Математического Обеспечения

ПетрГУ | ИМиИТ | О кафедре | Проекты | Лаборатория ИТС | Семинары НФИ/AMICT
Сотрудники | Учебный процесс | Табель-календарь | Курсовые и выпускные работы
Вычислительные ресурсы | Публикации | Архив новостей | Контактная информация (English)

Список заданий и контрольные сроки сдачи

Лабораторные занятия проводятся в дисплейных классах. Всего лабораторных работ — 7 (описание и сроки приведены ниже). Каждая работа засчитывается при удовлетворении всем требованиям протокола оценки и может быть оценена в зависимости от срока защиты. За каждую неделю задержки базовая оценка за работу уменьшается вдвое. Для получения зачета должны быть сданы все работы, количество набранных по результатам защиты работ баллов участвует в общей экзаменационной оценке (40%).

Сдача работы включает в себя:

Работающая программа является необходимым, но недостаточным условием сдачи лабораторной работы. Неспособность студента логически объяснить свое решение ведет к отрицательной оценке второго пункта протокола.

Целью выполнения лабораторных работ является получение студентом представлений о написании качественного кода на языке Си.

Задача 1. Калькулятор возраста

Базовая оценка: 4
Срок сдачи: 27.09

Предлагается шаблон программы "Hello, world" (hello.c):
/**
 * 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;
}
 
И шаблон Makefile (Makefile):
# цель по умолчанию (при вызове 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
 

Необходимо выполнить следующие действия вместе с инструктором:

  1. Подготовить каталог для программы
  2. Скопировать код программы в каталог
  3. Выполнить сборку и запуск вручную
  4. Выполнить сборку посредством Makefile (отдельно и из emacs)
  5. Внести намеренную ошибку, разобрать диагностическое сообщение, обратить внимание на номер строки с ошибкой
  6. Выполнить программу в отладчике:
    • выполнить по шагам, отслеживая значение 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

Реализовать программу, позволяющую на тестовых примерах проверить существование постоянной Капрекара, то есть числа, к которому сходится следующий алгоритм:

  1. Возьмем произвольное n-значное число, в котором не все цифры равны
  2. Получим новое число, сортировкой цифр по убыванию
  3. Получим второе новое число, сортировкой цифр по возрастанию
  4. Вычтя из первого числа второе, получим новое
  5. Если результат не равен исходному числу, перейти к п.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.

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

Имя файла с программой в кодах передается аргументом командной строки, если опущен — подразумевается стандартный ввод.

Студентам предложен шаблон кода считывания команды и анализ с помощью оператора 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.

Синтаксис высокоуровневого языка:

Пример
Вход:
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. Дополнительно реализовать специальные эффекты:

Каждому студенту придумать еще один эффект и реализовать. Каждый эффект должен быть реализован в виде отдельной функции.

Даны прототипы и код некоторых функций, которые рекомендуется использовать:

/*
 * Отразить левую половину табло на правую.
 */
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;
}