Файл test.txt
(пример): "абитуриент сможет перейти через барьер, и
наконец стать настоящим человеком, который поступит в вуз и будет
переходить с курса на курс, так что абитуриент в конце концов станет
настоящим специалистом — инженером!"
Обратите внимание на использование в этой программе функций rename
и remove из стандартной библиотеки, описываемой заголовочным
файлом stdio.h, для переименования и удаления файлов. В программе
сначала создается промежуточный файл test.bak, в который из
исходного файла копируется вся информация, но слова «абитуриент»
106
заменяются словами «студент», потом исходный файл удаляется, а
промежуточный получает имя исходного файла — test.txt.
Контрольные вопросы
1.
Что такое файловый дескриптор в программе на языке Си? В чем
отличие дескриптора от имени файла?
2.
Как открыть файл в программе на языке Си? Как задать режим доступа
к файлу?
3.
Как используется NULL при открытии файла в программах на Си?
4.
В чем заключается применение функций remove() и rename()?
5.
Нужно ли закрывать файл после использования? C помощью какой
функции это можно сделать?
Сумма нечетных на языке Си
Итак, мы уже располагаем некоторыми знаниями о языке
программирования Си. Вернемся к примеру, рассмотренному во введении
книги. Напишем программу суммирования нечетных членов числовой
последовательности.
Предположим, что числа вводятся пользователем с клавиатуры — столько,
сколько необходимо, пока не будет введен ноль, служащий ограничителем
последовательности.
/*
Сумма нечетных */
#include
int main()
{
int a,s=0; /*s —
сумма, обнуляем*/
printf("Программа нахождения суммы нечетных членов числового
ряда\n");
printf("Вводите целые числа или ноль для завершения\n");
do
{
printf(">");scanf("%d",&a);
if (a%2) s+=a; /*
экивалентно if (a%2!=0) s=s+a;*/
} while (a!=0); /* можно просто while(a); но это менее понятно*/
printf("сумма нечетных среди введенных =%d\n",s);
return 0;
}
Обратите внимание на некоторые особенности программы. В ней
107
соблюдены правила хорошего тона: после запуска до пользователя
доводится краткая информация о ее предназначении и делается
приглашение к вводу чисел. При этом для каждого очередного числа
выдается >. Вывод приглашения с помощью printf() и ввод с помощью
scanf()
сгруппированы в одной строке, что вполне логично.
Используется цикл do (ДО) для ввода чисел и подсчета суммы нечетных.
Для определения четности/нечетности введенного числа используется
встроенная в язык операция % взятия остатка от деления. Если остаток от
деления на 2 ненулевой — число нечетное, в противном случае — четное.
Поскольку в языке программирования Си нет выделенного логического
типа и любое ненулевое число воспринимается как логическая ИСТИНА,
можно записать if (a%2) вместо if (a%2!=0). Этим, как и
сокращенной записью s+=a вместо s=s+a, соблюден дух Си. Однако это
несколько ухудшает прозрачность программы, и при проверке
ограничителя цикла подобный трюк не применяется.
Напишем теперь второй вариант программы. В нем сначала будем
запрашивать пользователя о количестве подлежащих обработке чисел.
Кроме того, вместо условного оператора if применим условное
выражение ?:
/* Сумма нечетных вторым способом */
#include
#define MAXN 1000 /* максимальная длина последовательности чисел */
int main()
{
int a,i,n,s=0; /*s —
сумма, обнуляем*/
printf("Программа нахождения суммы нечетных членов числового
ряда\n");
printf("Введите количество чисел (<%d):",MAXN);scanf("%d",&n);
for (i=0;i {
printf("введите очередное число>");scanf("%d",&a);
s+=((a%2)?a:0); /* прибавляем или само число a, или ноль */
}
pri
ntf("сумма нечетных среди введенных =%d\n",s);
return 0;
}
В этом варианте следует обратить внимание на ввод ограничения сверху на
длину обрабатываемой числовой последовательности с помощью
директивы #define. Затем у пользователя запрашивается действительное
108
количество чисел, подлежащих обработке, n. Для ввода и обработки
используется единый цикл for, в котором переменная цикла i меняется от
0 до n – 1 путем записи for (i=0;iСи, поскольку в нем с помощью for часто обрабатываются массивы, а в
них нумерация начинается с нуля. Всего получится n повторений, что нам
и надо. После ввода числа к s добавляется результат вычисления
условного выражения. Сначала вычисляется остаток от деления a на 2.
Этот результат по правилам языка программирования Си воспринимается
как логическая ИСТИНА, если остаток ненулевой, иными словами, число
нечетное, и как логическая ЛОЖЬ, если нулевой, то есть число четное. В
первом случае все условное выражение принимает значение, указанное до
двоеточия, а именно само число a, во втором случае — ноль, указанный
после двоеточия. Полученное число прибавляется к s, давая возможность
получить нужный результат.
Контрольные вопросы
1.
Как работает оператор if (a%2)? Какая особенность работы с
логическими значениями языка программирования Си используется в
данной программе?
2.
С какими целями используется директива препроцессора #define?
3.
Поясните, как работает оператор s+=((a%2)?a:0). Могли бы вы
предложить альтернативные варианты подсчета суммы нечетных на
языке программирования Си?
Сортировка массивов
Весьма важную роль в программировании имеют задачи поиска и
сортировки. Неудивительно, что один из томов свого знаменитого труда
«Искусство программирования» Дональд Кнут так и назвал —
«Сортировка и поиск» [10].
Мы не будем сейчас углубляться в теорию, а интересующихся вопросом
отсылаем к упомянутой книге. Заметим лишь, что решать эти задачи
можно различными способами, причем существенно различающимися
между собой по эффективности — времени выполнения алгоритма и
требуемому объему памяти.
Приведем здесь лишь один из примеров реализации сортировки на языке
программирования Си — не самого эффективный, но прозрачный
алгоритм (сортировка выбором), в котором сначала на первое место в
массиве ставится наименьший элемент путем перебора всех элементов,
сравнения их с первым и перестановки в случае необходимости, а затем
процедура повторяется для второго и оставшихся элементов.
109
/* сортировка перестановками на Си */
#define N 10 /* размер обрабатываемого массива */
int main()
{
int a[N];
int i,j,buf;
printf("Программа сортировки массива\n");
printf("Введите %d чисел:",N);
for (i=0;i
for (i=0;i for (j=i+1;j if (a[i]>a[j])
{
buf=a[i];
a[i]=a[j];
a[j]=buf;
}
printf("
После сортировки:\n");
for (i=0;i printf("\n");
return 0;
}
В программе используется #define для задания размера массива.
Поскольку обработка с помощью директив препроцессора происходит до
компиляции, запись int a[N] допустима, несмотря на поддержку лишь
статических массивов в Си — к моменту выделения памяти N будет
заменено в программе конкретным числом.
Для сортировки используются два цикла, один из которых вложен в
другой. Внешний цикл использует переменную i для выбора сначала
первой позиции, куда помещается наименьший в массиве, затем — второй
и так до позиции N – 2 (именно это значит запись iцикле, использующем переменную j, ее значение изменяется от i + 1 до
N – 1 (j), производятся перебор всех оставшихся элементов массива и
их сравнение с находящимся на выбранном месте, в случае необходимости
выполняется перестановка. Поскольку в языке программирования Си нет
операции обмена значений переменных, для перестановки используется
110
буфер buf и «трехходовка» через него (в отличие от ассемблера x8086).
Контрольные вопросы
1.
Как работает оператор if (a%2)? Какая особенность работы с
логическими значениями языка программирования Си используется в
данной программе?
2.
Какие методы сортировки вам известны? Какие свои идеи вы бы
использовали для этого?
Система управления базой данных о студентах
Рассмотрим более сложную, нежели приведенные ранее, программу на
языке Си, а именно — систему управления базой данных (СУБД) о
студентах. Как правило, в современной практике для создания программ,
основными функциями которых являются хранение, поиск и выдача по
запросу пользователя информации, — информационных систем
используются специализированные средства.
Однако с учебными целями мы проиллюстрируем, как реализовать
простую базу данных непосредственно на универсальном языке
программирования, каковым является Си. Хранить информацию будем в
формируемом программой на диске компьютера файле, например
Students.dat
.
Какую информацию нужно хранить о студенте? Очевидно, что нужно
запоминать фамилию, имя и отчество, вероятно, год рождения (это может
представлять интерес для военкомата). Логично для студента сохранять
группу и успеваемость, которую можно выразить средним баллом,
полученным в ходе сдачи предыдущей сессии. Добавим — чтобы внести
положительных эмоций — еще размер стипендии и этим в нашем учебном
примере ограничимся (читателю не возбраняется создать свою версию
данной программы с расширенным перечнем характеристик студента,
например, указанием пола, длины волос, любимой рок-группы и т. д.).
Соответствующая структура данных может выглядеть так:
/* Описание структуры данных "Студент" */
struct
{
char Family[50]; //
Фамилия
char Imy[50]; //
Имя
char Otcestvo[50]; //
Отчество
char NGr[7]; //
Номер группы
int GodR
; // Год рождения
float SrBall
; // Средний балл
111
float Stip
; // Размер стипендии
} Stud;
Для удобства дальнейшей обработки фамилия, имя и отчество хранятся в
виде отдельных строк. Обозначение группы во многих вузах может
включать как цифры, так и буквы, поэтому номер группы тоже
представлен строкой NGr. Год рождения вполне может представляться
целым числом. Средний балл и размер стипендии —числа с плавающей
точкой.
Предположим, что после запуска программа будет выдавать на экран
«меню» — перечень возможных действий с базой данных.
Реализуем следующие основные операции с базой:
1.
Добавление записи о студенте в базу.
2.
Поиск студента по фамилии.
3.
Поиск по группе — вывод всех учащихся заданной группы.
4.
Поиск по возрасту — с указанием диапазона годов рождения «от» и
«до».
5.
Поиск по успеваемости с заданием диапазона среднего балла.
6.
Удаление данных.
7.
Выход из СУБД.
Логично соответствующим образом структурировать программу — для
каждого основного режима работы реализовать отдельную функцию.
Главная программа при этом содержит цикл, в котором производятся
запрос выбираемого режима, обработка и т. д., пока не будет выбран пункт
«Выход».
Уместным в данной ситуации представляется использование так
называемого нисходящего проектирования программ при разработке
системы. Оно заключается в том, что сначала пишется функция main с
меню, из которой вызываются функции Dobavl(), Udal() и т. д. Затем
создаются «заглушки» этих функций — заголовки с пустыми телами,
имеющими лишь return. Далее разрабатывается и отлаживается одна из
подлежащих реализации функций, затем — другая (остальные остаются
заглушками) и т. д. При этом на каждом этапе мы имеем работоспособную
программу, которую можно запустить на выполнение, хотя и с
ограниченной функциональностью!
Достарыңызбен бөлісу: |