1 Студенты заочной формы обучения выполняю ... варианта задания по последней цифре шифра зачетной книжки (стр. 12).

advertisement
1
Студенты заочной формы обучения выполняю лабораторную работу. Выбор номер
варианта задания по последней цифре шифра зачетной книжки (стр. 12).
Лабораторная работа
Теория.
Процессы. Все современные компьютеры могут делать одновременно несколько дел.
Например, одновременно с запущенной пользователем программой может выполняться
чтение с диска и вывод текста на экран монитора или на принтер. Для реализации
возможности параллельного выполнения нескольких пользовательских (или системных)
задач в современных операционных системах было введено понятие "процесс". Процессом
является выполняемая программа, включая текущие значения счетчика команд, регистров и
переменных, также для процесса ведется учет ресурсов операционной системы (открытые
файлы, текущий каталог и т.п.). Так как число физических процессоров обычно всегда
меньше общего числа запущенных процессов, реальный процессор переключается с
процесса на процесс, предоставляя каждому из них небольшой промежуток времени (квант)
для выполнения машинных инструкций текущего процесса, создавая иллюзию параллельной
работы всех запущенных процессов.
В машине 3 процессора
А
B
В машине 1 процессор
А
C
время
время
B
C
А
B
C
а)
б)
рис. 1
Как видно на рисунке 1-а в машине имеется три физических процессора и три
пользовательских приложения, в этом случае естественно поручить каждому из имеющихся
процессоров по одному процессу для выполнения. В случае 1-б у нас в распоряжении всего
один физический процессор, в этом случае процессы будут выполняться последовательно,
уступая время от времени физический процессор.
Потоки.
Модель
процесса
базируется
на
двух
независимых
концепциях:
2
группирования ресурсов и выполнения программы. Иногда полезно их разделять, и тут
появляется понятие потока.
С одной стороны процесс можно рассматривать как способ объединения родственных
ресурсов в одну группу. У процесса есть адресное пространство, содержащее тест
программы и данные, а также другие ресурсы. Ресурсами являются открытее файлы,
дочерние процессы, необработанные аварийные сообщения, обработчики сигналов, учетная
информация и многое другое. Гораздо проще управлять ресурсами, объединив их в форме
процесса.
С другой стороны, процесс можно рассматривать как поток исполняемых команд или
просто "поток". У потока есть счетчик команд, регистры процессора и стек.
Концепция потоков добавляет к модели процесса возможность одновременного
выполнения в одной и той же среде процесса нескольких программ, в достаточной мере
независимых. Несколько потоков, работающих параллельно в одном процессе, аналогичны
нескольких процессам, идущим параллельно на одном компьютере. В первом случае потоки
разделяют адресное пространство, открытые файлы и другие ресурсы. Во втором случае
процессы совместно пользуются физической памятью, дисками, принтерами и другими
ресурсами.
Создание потока. При запуске процесса операционная система создает адресное
пространство для процесса, начальный набор системных ресурсов и первичный поток. Поток
начинает выполнение и по завершении потока процесс считается завершившимся,
операционная система освобождает выделенные под процесс ресурсы.
Любой поток во время своей работы может создавать новые потоки, которые будут
выполняться в контексте процесса. В случае операционной системы Windows имеется
функция WinAPI:
#include <windows.h>
HANDLE CreateThread(PSECURITY_ATTRIBUTES psa, DWORD cbStack, PTHREAD_START_ROUTINE
pfnStartAddr, PVOID pvParam, DWORD tdwCreate, PDWORD pdwThreadID);
Параметры:
psa
Указатель на структуру SECURITY_ATTRIBUTES – параметры безопасности процесса.
Чтобы присвоить атрибуты защиты по умолчанию, передайте в этом параметре NULL.
cbStack
Размер стека потока или 0 – если хотите использовать значение по умолчанию
pfnStartA
ddr
Определяет адрес функции потока. PTHREAD_START_ROUTINE объявлена как
DWORD (WINAPI *PTHREAD_START_ROUTINE) (
LPVOID lpThreadParameter
);
3
т.е. функция потока – это функция возвращающая значение DWORD и принимающая
один параметр LPVOID. Обратите внимание на атрибут WINAPI – вы должны указать
его при определении своей функции потока (фактически этот атрибут указывает способ
передачи аргументов функции через стек по методу stdcall).
pvParam
Параметр, который будет передан в первом аргументе функции потока (т.е.
lpThreadParameter)
tdwCreat
e
Определяет дополнительные флаги, управляющие созданием потока. Если указать
CREATE_SUSPENDED, то поток будет создан, инициализирован и приостановлен.
pdwThre
adID
Выходной параметр. Это адрес переменной типа DWORD, в которой функция
возвращает идентификатор, приписанный системой новому потоку.
значени
е
функции
Дескриптор объекта ядра "поток", который может быть использован в других функциях
WinAPI для управления потоком. Или код ошибки, если значение равно NULL –
расширенную информацию об ошибке можно получить, вызвав GetLastError.
Завершение потока. Поток можно завершить следующими способами:
1.
функция потока возвращает управление (рекомендуемый способ);
2.
поток
самоуничтожается
вызовом
функции
ExitThread
(нежелательный
способ);
3.
один из потоков данного или стороннего процесса вызывает функцию
TerminateThread (нежелательный способ);
4.
завершается процесс, содержащий данный поток (тоже нежелательно).
Возврат управления функцией потока . Функцию потока следует проектировать так,
чтобы поток завершался только после того, как она возвращает управление. Это
единственный способ, гарантирующий корректную очистку всех ресурсов, принадлежавших
Вашему потоку. При этом:

любые С++-объекты, созданные данным потоком, уничтожаются
соответствующими деструкторами;

система корректно освобождает память, которую занимал стек потока;

система
устанавливает
код
завершения
данного
потока
(поддерживаемый объектом ядра "поток») — его и возвращает Ваша функция потока;

счетчик пользователей данного объекта ядра "поток" уменьшается на 1.
Функция ExitThread. Поток можно завершить принудительно, вызвав:
VOID ExitThread(DWORD dwExitCоde);
При этом освобождаются все ресурсы операционной системы, выделенные данному
потоку, но C/C++-pеcypcы (например, объекты, созданные из С++-классов) не очищаются
Именно поэтому лучше возвращать управление из функции потока, чем самому вызывать
функцию ExitThread.
4
В параметр dwExitCode Вы помещаете значение, которое система рассматривает как
код завершения потока. Возвращаемого значения у этой функции нет, потому что после ее
вызова поток перестает существовать.
Функция TerminateThread. Вызов этой функции также завершает поток:
BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);
В отличие от ExitThread, которая уничтожает только вызывающий поток, эта функция
завершает поток, указанный в параметре hThread. В параметр dwExitCode Вы помещаете
значение, которое система рассматривает как код завершения потока. После того как поток
будет уничтожен, счетчик пользователей его объекта ядра "поток" уменьшится на 1
Примечание
TerminateThread — функция асинхронная, т.e. она сообщает системе, что Вы хотите завершить
поток, но к тому времени, когда она вернет управление, поток может быть еще не уничтожен. Так что,
если Вам нужно точно знать момент завершения потока, используйте WaitForSingleObject или
аналогичную функцию, передав ей описатель этого потока.
Корректно написанное приложение не должно вызывать эту функцию, поскольку
поток не получает никакого уведомления о завершении; из-за этого он не может вы полнить
должную очистку ресурсов.
Особенность написания многопоточных приложений в Microsoft Visual C.
Microsoft поставляет с Visual С++ шесть библиотек С/С++. Их краткое описание пред
ставлено в следующей таблице.
Имя
библиотеки
Описание
LibC.lib
Статически подключаемая библиотека для однопоточных приложений
LibCD.lib
Отладочная версия статически подключаемой библиотеки для однопоточных
приложений
LibCMt.lib
Статически подключаемая библиотека для многопоточных приложений
LibCMtD.lib
Отладочная версия статически подключаемой библиотеки для многопоточных
приложений
MSVCRt.lib
Библиотека импорта для динамического подключения рабочей
MSVCRtD.dll; поддерживает как одно-, так и многопоточные приложения
MSVCRtD.lib
Библиотека импорта дли динамического подключения отладочной версии
MSVCRtD.dll; поддерживает как одно-, так и многопоточные приложения
версии
При реализации любого проекта нужно знать, с какой библиотекой его следует
связать. Конкретную библиотеку можно выбрать в диалоговом окне Project Settings: на
вкладке С/С++ в списке Category укажите Code Generation, а в списке Use Run-Time Library
— одну из шести библиотек. Так как мы разрабатываем многопоточное приложение, то нам
подойдут либо LibCMt.lib, MSVCRt.lib, если мы делаем рабочую версию программы, либо
LibCMtD.lib, MSVCRtD.lib, если мы делаем отладочную версию.
Так как стандартная библиотека С создавалась в те времена, когда о многопоточности
не было и речи, некоторые функции библиотеки не являются безопасными с точки зрения
5
одновременного доступа из нескольких потоков. Например, чтобы узнать код ошибки
выполнения предыдущей функции, используется переменная errno (т.е. объявление int errno).
Любой поток может вызывать библиотечные функции независимо от других потоков, а
значит и код ошибки должен быть уникален для потока, а не для процесса. Поэтому в
мультипоточной версии errno уже не переменная а функция, т.е. объявление выглядит
примерно так: int errno().
Чтобы многопоточные программы, использующие библиотеку С/С++, работали
корректно, потоки нужно создавать с помощью специальной библиотечной функции:
#include <process.h>
unsigned long _beginthreadex( void *security, unsigned stack_size,
unsigned (*start_address)(void *), void *arglist, unsigned initflag, unsigned *thrdaddr)
Назначение параметров функции _beginthreadex совпадает с назначением параметров
CreateThread. Функция возвращает дескриптор объекта ядра "поток" или -1 в случае ошибки
– код ошибки можно узнать через errno.
Заметьте, что функция _beginthreadex существует только в многопоточных версиях
библиотеки С/С++. Связав проект с однопоточной библиотекой, Вы получите от
компоновщика сообщение об ошибке "unresolved external symbol». Конечно, это сделано
специально, потому что однопоточная библиотека не может корректно работать в
многопоточном приложении. Также обратите внимание на то, что при создании нового
проекта Visual Studio по умолчанию выбирает однопоточную библиотеку. Этот вариант не
самый безопасный, и для многопоточных приложений Вы должны сами выбрать одну из
многопоточных версий библиотеки С/С++.
Аналогично завершение потока рекомендуется делать выходом из функции потока,
либо с помощью специальной библиотечной функции _endthreadex.
Ожидание завершения потока. В WinAPI существует семейство функций, которые
позволяют потоку выполнить ожидание некоторого события (или событий), во время
ожидания поток засыпает. Первая функция позволяет ждать наступления одного события:
DWORD WaitForSingleObject( HANDLE hObject, DWORD dwMilliseconds);
Параметры:
hObject
дескриптор объекта ядра, поддерживающий состояния «свободен-занят»
dwMilleseco
nds
указывает, сколько времени (в миллисекундах) поток готов ждать освобождения
объекта или INFINITE – бесконечное ожидание
результат
функции
WAIT_OBJECT_0 – объект перешел в сигнализированное состояние
WAIT_TIMEOUT
– время ожидания истекло
WAIT_FAILED
– неправильный вызов функции, подробная информация доступна
6
через GetLastError()
Вторая функция WaitForMultipleObjects предназначения для ожидания нескольких
событий и возвращает управление, когда любое (или все) из указанных событий наступило:
DWORD WaitForMultipleObjects( DWOHD dwCount, CONST HANDLE* lpHandles, BOOL fWaitAll,
DWORD dwMilliseconds);
Параметры:
dwCount
число объектов в массиве lpHandles
lpHandles
массив дескрипторов объектов ядра, поддерживающих состояния «свободензанят»
fWaitAll
если TRUE – ждать освобождения всех занятых объектов в массиве
dwMillisecon
ds
указывает, сколько времени (в миллисекундах) поток готов ждать освобождения
объектов или INFINITE – бесконечное ожидание
результат
функции
WAIT_OBJECT_0 – объект № 0 перешел в сигнализированное состояние
…
WAIT_OBJECT_0 + (dwCount – 1) объект № (dwCount – 1) перешел в
сигнализированное состояние
WAIT_TIMEOUT
– время ожидания истекло
WAIT_FAILED
– неправильный
доступна через GetLastError()
вызов
функции,
подробная
информация
Следующие объекты ядра бывают в свободном или занятом состоянии:

процессы
 потоки
 задания
 файлы
 консольный ввод
 уведомления об изменении файлов
 события
 ожидаемые таймеры
 семафоры
 мьютексы
Для объектов ядра типа "поток" занятое состояние означает, что поток выполняется, а
состояние свободен - поток завершился. Применение функций ожидания для других типов
объектов мы рассмотрим позднее.
Пример многопоточного приложения на Microsoft Visual C. Напишем простое
многопоточное приложение, где основной поток создает второй поток который выводит на
экран сообщение, указатель на которое будет передан функции потока.
7
#include
#include
#include
#include
<windows.h>
<process.h>
<stdio.h>
<stdlib.h>
const char * message = "Hello, world!\n";
unsigned int WINAPI thread_proc(char *msg) {
printf("New thread says: %s\n", msg);
return 0;
}
int main() {
HANDLE thread;
thread = (HANDLE)_beginthreadex(NULL, 0, thread_proc, (void*)message, 0,
NULL);
if (WAIT_OBJECT_0 != WaitForSingleObject(thread, INFINITE))
printf("Wait failed. Error code 0x%08X\n", GetLastError());
return 0;
}
Усложним пример. Мы создадим несколько потоков, каждый из которых будет писать на экран
некоторое сообщение. Основной поток будет ждать окончание потоков, и извещать об этом
пользователя.
#define _CRT_RAND_S
#include
#include
#include
#include
<windows.h>
<process.h>
<stdio.h>
<stdlib.h>
#define N_THREADS 5
unsigned int WINAPI thread_proc(void *arg) {
size_t num = (size_t)arg;
unsigned int rvalue;
rand_s(&rvalue);
rvalue = (2 * rvalue % N_THREADS) + 1;
printf("Thread %d: sleeping for %d seconds\n", num, rvalue);
Sleep(rvalue * 1000);
return 0;
}
int main() {
int i, nthreads;
HANDLE threads[N_THREADS];
int nums[N_THREADS];
DWORD r;
for (i = 0; i < N_THREADS; i++) {
threads[i] = (HANDLE)_beginthreadex(NULL, 0, thread_proc, (void*)i,
0, NULL);
nums[i] = i;
}
nthreads = N_THREADS;
while (nthreads > 0) {
r = WaitForMultipleObjects(nthreads, threads, FALSE, INFINITE);
8
if ((r >= WAIT_OBJECT_0) && (r < WAIT_OBJECT_0 + nthreads)) {
i = r - WAIT_OBJECT_0;
printf("Thread %d exited\n", nums[i]);
memcpy(&threads[i], &threads[i + 1], (nthreads - i - 1) *
sizeof(threads[0]));
memcpy(&nums[i], &nums[i + 1], (nthreads - i - 1) *
sizeof(nums[0]));
nthreads--;
} else {
printf("Wait failed. Error code 0x%08X\n", GetLastError());
}
}
return 0;
}
В данном приложении мы создаем потоки с помощью функции _beginthreadex,
используя одну и ту же функцию потока thread_proc. Через 4-ый параметр _beginthreadex
мы передаем параметр для функции нового потока, в нашем случае – это условный
порядковый номер создаваемого потока.
Функция потока приводит полученный указатель к номеру потока и засыпает на
случайное время. Основной поток использует функцию WaitForMultipleObjects для того,
чтобы выполнить ожидание нескольких событий, в нашем случае - это завершение потока,
функция возвращает номер объекта, который сигнализировал событие.
Синхронизация
потоков.
Операционная
система
Windows
предоставляет
программисту целый набор примитивов для синхронизации потоков. Это:
событие – объект ядра, который имеет два состояния: "не активирован" и
"активирован";
семафор – объект ядра, с которым ассоциировано целочисленное значение.
Используя функции ожидания можно уменьшить значение семафора на единицу.
При попытке уменьшить семафор с нулевым значением, поток засыпает и ждет, пока
кто-то другой не освободит семафор;
мьютекс (а точнее рекурсивный мьютекс) – объект ядра, который гарантирует
потокам взаимоисключающий доступ к единственному ресурсу;
критическая секция – схожа по выполняемым задачам с мьютексом, но имеется ряд
незначительных отличий.
Объект ядра "событие". События уведомляют об окончании какой-либо операции.
Объекты-события бывают двух типов: со сбросом вручную (manual-reset events) и с
автосбросом (auto-reset events). Первые позволяют возобновлять выполнение сразу
нескольких ждущих потоков, вторые — только одного. Объекты-события обычно используют
в том случае, когда какой-то поток выполняет инициализацию, а затем сигнализирует
другому потоку, что тот может продолжить работу. Инициализирующий поток переводит
объект "событие" в занятое состояние и приступает к своим операциям. Закончив, он
сбрасывает событие в свободное состояние. Тогда другой поток, который ждал перехода
9
события в свободное состояние, пробуждается и вновь становится выполняемым.
Объект ядра "событие" создается функцией CreateEvent:
HANDLE CreateEvent(PSECURITY_ATTRIBUTES psa, BOOL fManualReset, BOOL fInitialState,
PCTSTR pszName);
Параметры:
psa
Указатель на структуру SECURITY_ATTRIBUTES – параметры безопасности
процесса. Чтобы присвоить атрибуты защиты по умолчанию, передайте в этом
параметре NULL.
fManualReset
TRUE – событие со сбросом вручную, FALSE – событие с атосбросом
fInitialState
Начальное состояние события — свободное (TRUE) или занятое (FALSE)
pszName
Имя объекта ядра, используется для межпроцессного взаимодействия или
NULL – без имени
результат
функции
Дескриптор объекта ядра или NULL – в этом случае подробности об ошибке
можно получить с помощью GetLastError()
Открыть уже существующее событие по его имени:
HANDLE OpenEvent( DWORD fdwAccess, BOOL fInhent, PCTSTR pszName);
Параметры:
fdwAccess
Запрашиваемые права доступа к объекту. Может принимать следующие значения:
SYNCHRONIZE – право использовать объект для синхронизации (в Wait-функциях)
EVENT_MODIFY_STATE – право на модификацию состояния объекта (активация
события)
также можно использовать "обобщенные" права доступа:
GENERIC_WRITE – право на запись, т.е. модификацию
fInhent
TRUE – наследовать дескриптор в дочерних процессах
pszName
Имя объекта ядра. Объект с таким именем должен уже существовать к моменту
вызова функции.
результат
функции
Дескриптор объекта ядра или NULL – в этом случае подробности об ошибке можно
получить с помощью GetLastError()
Ненужный объект ядра "событие" следует закрыть вызовом CloseHandle.
Чтобы перевести объект событие в свободное состояние (т.е. "активировать" событие),
необходимо вызывать:
BOOL SetEvent(HANDLE hEvent);
А чтобы поменять его на занятое (т.е. "деактивировать"):
BOOL ResetEvent(HANDLE hEvent);
Поток может выполнить ожидание перехода объекта в свободное состояние (т.е. в состояние
"активирован") с помощью функций ожидания MsgWaitForSingleObject или MsgWaitForMultipleObject.
10
Для событий с автосбросом действует следующее правило: когда его ожидание потоком
успешно завершается, этот объект автоматически сбрасывается в занятое состояние. Отсюда
и произошло название таких объектов-событий. Для этого объекта обычно не требуется
вызывать ResetEvent, поскольку система сама восстанавливает его состояние, а для событий
со сбросом вручную никаких побочных эффектов успешного ожидания не предусмотрено,
т.е. объект останется в свободном состоянии.
Пример использования объекта "событие". В данном примере главный поток
процесса создает два дочерних потока. Первый ждет наступление события, а второй –
устанавливает это событие в сигнальное состояние по истечении небольшого промежутка
времени.
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
<windows.h>
<process.h>
<stdio.h>
<stdlib.h>
HANDLE Event;
unsigned int WINAPI thread1(void *unused) {
printf("Thread #1 started.\n");
if (WAIT_OBJECT_0 != WaitForSingleObject(Event, INFINITE)) {
printf("Wait function failed. Error code 0x%08X\n", GetLastError());
abort();
}
printf("Thread #1 exited.\n");
return 0;
}
unsigned int WINAPI thread2(void *unused) {
printf("Thread #2 started.\n");
Sleep(10 * 1000); // Sleep for 10 seconds
if (!SetEvent(Event)) {
printf("SetEvent function failed. Error code 0x%08X\n",
GetLastError());
abort();
}
printf("Thread #2 exited.\n");
return 0;
}
int main(void)
{
int i;
HANDLE threads[2];
if (NULL == (Event = CreateEvent(NULL, FALSE, FALSE, NULL))) {
printf("Cannot create the event. Error code 0x%08X\n",
GetLastError());
abort();
}
printf("Creating 2 threads.\n");
for (i = 0; i < 2; i++) {
11
threads[i] = (HANDLE)_beginthreadex(NULL, 0, (i == 0) ? thread1 :
thread2, NULL, 0, NULL);
if ((HANDLE)-1 == threads[i]) {
printf("Failed to create a thread. Errno %d\n", errno);
abort();
}
}
printf("Waiting for threads to complete.\n");
if (WAIT_FAILED == WaitForMultipleObjects(2, threads, TRUE, INFINITE))
printf("Wait failed. Error code 0x%08X\n", GetLastError());
printf("All threads exited.\n");
for (i = 0; i < 2; i++)
CloseHandle(threads[i]);
CloseHandle(Event);
return 0;
}
12
Выбор номер задания по последней цифре шифра зачетной книжки.
Задания для самостоятельной работы
Требования. Необходимо создать приложение, в котором существует два потока:
интерактивный поток – выполняет взаимодействие с пользователем и рабочий поток –
выполняет логику решения задачи. Это потоки создаются только один раз и завершаются
только при выходе из программы.
Взаимодействие потоков должно производиться по следующей схеме: рабочий поток
ждет поступления данных для обработки, интерактивный поток получает от пользователя
весь набор данных и информирует рабочий поток о необходимости обработать эти данные,
затем ждет, пока рабочий поток не выполнит поставленную задачу, после этого
интерактивный поток выводит на экран результат. После отображения результатов,
пользователю необходимо дать возможность выхода из программы или переход к повторному
вводу данных.
Все входные значения для программы должны задаваться интерактивно (а не
статически указанные значения в исходных кодах программы). Допустима/желательна
реализация пользовательского интерфейса средствами консольного ввода-вывода. В случае
если введенные данные являются недопустимыми, необходимо информировать об этом
пользователя. Если недопустимость (либо отсутствие решения) вытекает из особенностей
алгоритма (напр., несовпадение размерностей векторов при сложении), то такая проверка
должна выполняться в рабочем потоке.
Синхронизация потоков (уведомление рабочего потока о новых данных, уведомление
интерактивного потока о завершении обработки данных) должна осуществляться только
через объекты ядра – «событие».
Все вызовы функций библиотеки времени выполнения C/С++ и WinAPI, которые
могут вернут статус ошибки, должны проверяться на статус ошибки с выводом
соответствующей диагностики пользователю.
Вариант 1
Калькулятор, работающий с комплексными числами с плавающей точкой, и
реализующий следующие операции: сложение, умножение, деление и вычитание.
Реализовать обработку ошибок: деление на ноль.
Вариант 2
Виртуальная гадалка: в ответ на запрос пользователя о предсказании должна
возвращаться фраза с "предсказанием". Ограничиться набором 10-20 фраз. Реализовать
13
обработку ошибок: ошибка должна возникать случайным образом и означает, что гадалка не
знает ответ.
Вариант 3
На вход подается массив чисел, программа случайным образом перемешивает числа и
выводит результат на экран. Реализовать обработку ошибок: отрицательное число элементов.
Вариант 4
Англо-русский словарь: передается слово на английском языке – возвращается его
перевод. Ограничиться набором из 10 – 20 слов. Реализовать обработку ошибок: отсутствие
слова в словаре.
Вариант 5
Определение високосного года: на входе подается год, на выходе булево значение
результата. Реализовать обработку ошибок: неверное значение года (год <= 0)
Вариант 6
Генератор последовательности случайных чисел: на входе подается максимальное
значение случайного числа и число элементов – на выходе массив полученных чисел.
Реализовать обработку ошибок: отрицательное число элементов.
Вариант 7
см. вариант 22.
Вариант 8
Программа преобразования входной строки в кодировку BASE64 и обратно. Полная
спецификация BASE64 содержится в RFC 1421 и RFC 2045.
Для того, чтобы преобразовать данные в BASE64 , первый байт помещается в самые
старшие восемь бит 24-битного буфера, следующие в средние восемь и третий в младшие
значащие восемь бит. Если кодируется менее чем три байта, то соответствующие биты
буфера устанавливаются в ноль. Далее каждые шесть бит буфера, начиная с самых старших,
используются
как
индексы
«ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/»
строки
и
её
символы, на которые указывают индексы, помещаются в выходную строку. Если кодируются
только один или два байта, используются только первые два или три символа строки и
14
выходная строка дополняется двумя или одним символами «=». Это предотвращает
добавление дополнительных битов к восстановленным данным. Процесс повторяется над
оставшимися входными данными.
Вариант 9
Операции с двумя множествами целых чисел: объединение, пересечение, разность.
Вариант 10
Умножение двух матриц: на входе массивы произвольных размерностей (k,m) и (i,j).
Реализовать
обработку
ошибок:
невозможность
перемножения
матриц
вследствие
несовпадения размерностей.
Вариант 11
Замена вхождения одной последовательности в строке символов на другую
последовательность символов. Реализовать обработку ошибок: искомый шаблон не найден.
Вариант 12
Поиск числа в целочисленном массиве. Реализовать обработку ошибок: искомое число
не найдено.
Вариант 13
Замена порядка символов во входной строке на обратный.
Вариант 14
Генерация
последовательности
Фибоначчи:
на
входе
начальные
параметры
последовательности F0 и F1 и длина ряда. Формула ряда следующая: Fn = Fn-1 + Fn-2
Вариант 15
Решение линейного уравнения вида y = a*x + b. Входные параметры: a и b и у. На
выходе найденное значение x. Реализовать обработку ошибок: значение параметра a = 0.
Вариант 16
Игра "Угадай число": программа «загадывает» число, пользователь пытается угадать
это число, программа сообщает, что либо число угадано, либо больше или меньше
загаданное число относительно числа, указанного пользователем. Если пользователь заново
15
начинает игру, то программа загадывает новое число.
Вариант 17
Генерации монотонной последовательности простых чисел, начиная с 1. Программе
указывается
число
чисел
в
последовательности.
Реализовать
обработку
ошибок:
отрицательное число элементов.
Вариант 18
Нахождение действительных корней квадратного уравнения a*x2 + b*x + c = 0.
Реализовать обработку ошибок: отсутствие решения в действительных числах.
Вариант 19
Генерации геометрической прогрессии: на входе начальный член b1 и число q
(знаменатель геометрической прогрессии) и число генерируемых членов. Проверка на
ошибки: начальный член должен быть отличен от нуля. Формула ряда: bn = b1 q (n – 1)
Вариант 20
Интерфейс хеш-функции (или, иначе говоря, цифровой подписи):

получение хеш-значения для входной строки

проверка соответствия входной строки указанному хеш-значению
Указание: для простоты реализации рекомендуется выбрать в качестве хеш-значения
беззнаковое длинное целое (т.е. unsigned long int).
Вариант 21
Решение системы линейных уравнений вида:
a11x1 + a12x2 = b1
a21x1 + a22x2 = b2
На вход подается матрица A с коэффициентами aij и вектор B с коэффициентами bi; на
выходе – вектор Х, содержащий найденные значения xi. Реализовать проверку на ошибки:
отсутствие решения.
Вариант 22
Векторная арифметика: сложение, вычитание, скалярное произведение двух векторов.
Реализовать проверку на ошибки: несовпадение размерностей операндов.
16
Вариант 23
Получение
производной
многочлена
следующего
вида
a0 + a1x + a2x2 + … + anxn . На вход подается массив коэффициентов ai, на выходе bi –
коэффициенты результирующего полинома.
Вариант 24
Подсчета частоты встречаемости символов в текстовой строке: на вход подается
текстовая строка (ASCII-8, т.е. 8-битные строки), на выходе массив частот.
Вариант 25
Разбор текстовой строки, содержащей дату и время. Входная строка может иметь
следующие форматы:

"год-месяц-день часы:минуты:секунды"

"часы:минуты:секунды год-месяц-день"

"год/месяц/день часы:минуты:секунды"

"часы:минуты:секунды год/месяц/день"
На выходе шесть чисел, которые соответствуют году, месяцу, дню, и т.д. Реализовать
проверку на ошибки: неверный формат входной строки, неверное значение года
(отрицательное), месяца (вне диапазона 1..12), и т.д.
Вариант 26
Проверка принадлежности точки вещественной плоскости с координатами (X1, Y1) к
окружности, заданной координатами центра (X2, Y2) и радиусом R. Реализовать проверку на
ошибки: отрицательный радиус.
Вариант 27
Для каждого целого положительного числа N существует его представление в виде
произведения k взаимно простых сомножителей pi с показателем степени ni ≥ 1:
N = p1 1 p 2 2  p k k
n
n
n
Такое представление единственно, с точностью до порядка следования сомножителей.
Если, например, положить pi > pj, для i > j, тогда такое разложение становится полностью
определенным единственным образом.
Таким образом, необходимо реализовать программу разложения числа на простые
сомножители. На вход подается число N, на выходе – разложение числа на сомножители
17
согласно приведенной выше формуле.
Реализовать проверку на ошибки: N – отрицательное или нулевое число.
Вариант 28
Построение гистограммы. На вход подается массив целых чисел, начало отсчета
интервалов и размер интервала. Программа формирует массив, число элементов в котором
равно числу интервалов, а значения — число попавших чисел в каждый из интервалов и
выводит на экран этот массив. Реализовать проверку на ошибки: отрицательное число
элементов, отрицательный или нулевой размер интервала.
Вариант 29
Реализовать
программу
генерации
последовательность
чисел
на
основании
следующего алгоритма. Имеется некоторое число K (т.н. модуль) и некоторое начальное
значение p 1 отличное от нуля. Тогда для i > 1, pi = (pi 1 + p1 )mod K , где mod – остаток от
деления. Для некоторого значения j полученное значение p j будет совпадать с начальным
значением, т.е. p 1
– это служит признаком конца алгоритма. Таким образом, на вход
подаются входные параметры K и p 1 . На выходе – массив, содержащий полученную
последовательность. Реализовать проверку на ошибки: модуль меньше 2, начальное значение
меньше 1.
Вариант 30
Реализовать
программу,
которая
получает
массив
целых
чисел.
В
этой
последовательности необходимо найти простые числа. Найденные простые числа
необходимо вывести на экран.
Download