Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Основы разработки программного обеспечения на примере языка С. Учебное пособие для СПО

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
☆
Кроме того, абстрактные типы не удовлетворяют требованиям, предъявляемым встроенным переменным. Например, при передаче в качестве параметра по значению абстрактного типа, который на самом деле является указателем, имеется возможность изменить значение переданной переменной, что явно не видно из кода.
Еще одним неудобством становится необходимость практически всегда реализовывать операции сравнения и копирования для АТД и в пользовательской программе использовать только их. При этом наглядность таких действий несколько снижается. Строка
А=В;
выглядит понятнее, чем
fraction_copy(A,B);
Рассмотрим еще один пример реализации и использования абстрактного типа данных - множество. В некоторых языках программирования, например Modula-2, множество является стандартным типом данных, но в Си такой тип отсутствует. Для примера рассмотрим множество символов расширенной таблицы ASCII, т.е. элемен-тами множества могут быть любые символы char. Для хранения такого множества можно использовать массив с длиной, равной количеству всех значений char, т.е. 256 элементов. Для символов, принадлежащих конкретному множеству, значение элемента с номером, соответствующим ASCII-коду символа, устанавливается в единицу. Остальные элементы массива равны нулю.
Перед использованием множества следует обнулить массив, чтобы получить пустое множество. Далее путем добавления нужных элементов можно получить требуемое множество.
В некоторых случаях удобно сразу создать множество на основе строки символов, поместив все символы строки в результирующее множество.
Основные операции над множеством - это добавление элемента, проверка наличия элемента, объединение, вычитание и пересечение
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
141
множеств. Отсюда можно сформировать set.h-файл с определением типа и заголовками операций.
----- файл set.h ----­/******************************************************* date: 10 January 2013 description: Set type ******************************************************/ /* Definition of the Set type */ typedef struct { int elems[255]; } Set; /******************************************************* * Name : initSet * Purpose : prepares the Set to work with * (should be called before using Set variable) * Input : s – new set * Output : s – set prepared to work with * Return : none ******************************************************/ void initSet(Set *s); /******************************************************* * Name : initFromStringSet * Purpose : prepares the Set to work with * (can be called instead of initSet(…) ) * Input : s – new set, * str – string with elements to add to the set * Output : s – set prepared to work with, * containing elements from string str * Return : none ******************************************************/ void initFromStringSet(Set *s, char *str) /******************************************************* * Name : addElemSet * Purpose : adds the element to the set * (if element already exists – does nothing) * Input : s – set, elem – element to add * Output : s – set with added element
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
142
* Return : none ******************************************************/ void addElemSet(Set *s, char elem); /******************************************************* * Name : removeElemSet * Purpose : removes the element from the set * (if element already exists – does nothing) * Input : s – set, elem – element to delete * Output : s – set with no element elem * Return : none ******************************************************/ void removeElemSet(Set *s, char elem); /******************************************************* * Name : isInSet * Purpose : check whether the element is in the set * Input : s – set * Output : none * Return : * -1 is in set * 0 is not in set ******************************************************/ int isInSet(Set s, char elem); /******************************************************* * Name : unionSet * Purpose : unions two sets * Input : s1 – set one, s2 – set two * Output : s1 – union result * Return : none ******************************************************/ void unionSet(Set *s1, Set s2); // deducts set s2 from set s1 // input: s1 – set one, s2 – set two // output: s1 – deduct result void substrSet(Set *s1, Set s2); /******************************************************* * Name : intersectSet * Purpose : intersects two sets * Input : s1 – set one, s2 – set two * Output : s1 – intersection result
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
143
* Return : none ******************************************************/ void intersectSet(Set *s1, Set s2); /******************************************************* * Name : isEqualSet * Purpose : compares two sets * Input : s1 – set one, s2 – set two * Output : none * Return : * 1 sets are equal * 0 not equal ******************************************************/ int isEqualSet(Set s1, Set s2); /******************************************************* * Name : copySet * Purpose : copies set s1 to set s2 * Input : s1 – set one, s2 – set two * Output : s1 – copy of s2 * Return : none ******************************************************/ void copySet(Set *s1, Set s2); /******************************************************* * Name : isEmptySet * Purpose : check whether the set is empty * Input : s1 – set one, s2 – set two * Output : none * Return : * 1 set is empty * 0 not empty ******************************************************/ int isEmptySet(Set s); /******************************************************* * Name : printSet * Purpose : prints set * Input : s1 – set to print * Output : prints set on th screen (standard output) * Return : none ******************************************************/ void printSet(Set s);
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
144
----- файл set.c ----­// // author: O. // date: 10 January 2013 // description: Set type // #include "set.h" #include <stdio.h> void initSet(Set *s) { int i; for (i = 0; i < 255; i++) { (*s).elems[i] = 0; } } void initFromStringSet(Set *s, char *str) { int i; while(*str != '\0') { i = (int)*str; (*s).elems[i] = 1; str++; } } void addElemSet(Set *s, char elem) { int i; i = (int)elem; (*s).elems[i] = 1; } void removeElemSet(Set *s, char elem) { int i; i = (int)elem; (*s).elems[i] = 0; } int isInSet(Set s, char elem)
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
145
{ int i; i = (int)elem; if (s.elems[i] == 1) { return 1; } else { return 0; } } void unionSet(Set *s1, Set s2) { int i; for (i=0; i < 255; i++) { (*s1).elems[i] = ((*s1).elems[i] | s2.elems[i]); } } void substrSet(Set *s1, Set s2) { int i; for (i=0; i < 255; i++) { (*s1).elems[i] = ((*s1).elems[i] - s2.elems[i]); if ( (*s1).elems[i] < 0 ) { (*s1).elems[i] = 0; } } } void intersectSet(Set *s1, Set s2) { int i; for (i=0; i < 255; i++) { (*s1).elems[i] = ((*s1).elems[i] & s2.elems[i]); } }
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
146
int isEqualSet(Set s1, Set s2) { int i; for (i=0; i < 255; i++) { if (s1.elems[i] != s2.elems[i] ) { return 0; } } return 1; } void copySet(Set *s1, Set s2) { int i; for (i=0; i < 255; i++) { (*s1).elems[i] = s2.elems[i]; } } int isEmptySet(Set s) { int i; for (i=0; i < 255; i++) { if (s.elems[i] == 1 ) { return 0; } } return 1; } void printSet(Set s) { int i; for (i=0; i < 255; i++) { if (s.elems[i] == 1 ) {
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
147
printf("%c",(char)i); } } }
Пример использования множества показан далее. Он будет помещен в отдельный файл "setexample.c", в который будет импортироваться реализованный тип данных путем включения заголовочного файла "set.h".
----- файл setexample.c ----­/****************************************************** date: 10 January 2013 description: Set type *******************************************************/ #include <stdio.h> #include "set.h" int main() { Set s1, s2; /* Do not forget to do it! */ initFromStringSet(&s1, "abcde"); initSet(&s2); addElemSet(&s2,'a'); addElemSet(&s2,'b'); printf("\n s1: "); printSet(s1); printf("\n s2: "); printSet(s2); removeElemSet(&s1,'a'); printf("\n s1: "); printSet(s1); unionSet(&s1, s2); printf("\n s1: "); printSet(s1); substrSet(&s1, s2); printf("\n s1: "); printSet(s1);
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
148
addElemSet(&s1,'a'); intersectSet(&s1, s2); printf("\n s1: "); printSet(s1); return 0; }
Из реализации данного относительного несложного типа можно вывести ряд правил, которые необходимо учитывать при разработке АТД.
Во-первых, это необходимость в импортере всегда использовать операцию создания, которая в примере реализована функцией
initSet и функцией initFromStringSet. Так как массив в Си не
инициализируется по умолчанию, то если в основной программе перед работой со множеством не вызвать initSet (или
initFromStringSet), во множестве могут оказаться "лишние"
элементы. При этом компилятор сам не может отследить корректность использования функций создания. Это целиком задача программиста.
Во-вторых, необходимо самостоятельно реализовать функции копирования и сравнения.
При реализации функций объединения и пересечения множеств используются операции побитового ИЛИ и побитового И, что упрощает реализацию и одновременно отражает суть соответствующих операций над множествами.
Несмотря на то, что массивы эффективнее передавать по ссылке, чтобы не копировать большой объем информации в память стека при передаче параметров, в данном примере переменные типа Set передаются по ссылке только в те функции, где они должны быть изменены. Это уменьшает вероятность случайного изменения значения переменной типа Set в остальных функциях и позволяет отражать в самом заголовке функции, происходит ли внутри нее изменение множества или нет.
В качестве примера использования множества рассматривается задача
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
149
проверки символов введенной строки на допустимость. Пред-положим, необходимо проверить, является ли введенная строка шест­надцатеричным числом. Тогда можно сказать, что допустимый алфавит (т.е. допустимое множество) - это символы
0 1 2 3 4 5 6 7 8 9 A B C D E F
и все символы строки должны принадлежать этому алфавиту (множеству).
/* sample code - how Set can be used */ Set s3; char str[100]; int i; initFromStringSet(&s3,"0123456789ABCDEF"); printf("\nEnter HEX number: "); scanf("%s", str); i = 0; while(str[i] != '\0') { if (isInSet(s3, str[i]) == 0) { printf("You entered not a HEX number!"); break; } i++; }
5.5. Инкапсуляция
Сокрытие реализации структуры и операций называется инкапсуляцией. Разработав и реализовав единожды некий скрытый тип, можно больше никогда не возвращаться к этой задаче, а стать пользователем своего модуля, просто рассматривая его как "черный ящик".
Использование скрытых типов имеет следующие преимущества:
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
150
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]