- •Конспект лекций по курсу
- •Абстрактные структуры данных
- •Определение
- •Базовые структуры данных
- •Очереди и стеки
- •Деревья
- •Внутренние структуры данных
- •Отображение абстрактных структур данных на внутренние
- •Строка-вектор
- •1. Функция сцепления двух строк
- •2. Функция поэлементного сравнения двух строк
- •3. Функция разбиения строки.
- •4. Функция нахождения подстроки в строке
- •Строка-список
- •1. Сцепление двух строк
- •2. Поэлементное сравнение двух строк
- •3. Разбиение строки на части
- •4. Функция нахождения подстроки в строке
- •Стек-вектор
- •Стек-список
- •Очереди
- •Очередь-вектор
- •Очередь-список
- •Деревья
- •Классификация таблиц
- •Способ работы с таблицей
- •Способ доступа к таблице
- •Просматриваемые таблицы
- •Статическая просматриваемая таблица-вектор
- •Динамическая просматриваемая таблица-вектор
- •Просматриваемая таблица-список
- •Упорядоченные таблицы
- •Упорядоченная таблица-вектор
- •Динамическая упорядоченная таблица – вектор
- •Упорядоченная таблица – двоичное дерево
- •Перемешанные таблицы
- •Открытое перемешивание
- •Перемешивание сцеплением
3. Функция разбиения строки.
Для разбиения заданной строки stringна части необходимо указать правило разбиения и место размещения результата.
В качестве правила разбиения в рассматриваемой функции задается некоторый символ-разделитель полей (fs); сам этот символ в результирующие строки не записывается.
Функция должна сформировать и вернуть в качестве результата своей работы количество разбиений исходной строки и массив указателей на выделенные фрагменты строки. Поскольку количество разбиений неизвестно, функция сначала определяет его (т.е. подсчитывает, сколько раз в строке встречается символ-разделитель), затем выделяет необходимую память и формирует результат. При этом исходная строка сохраняет свой вид.
Алгоритм функции представлен на рис. II–16.

Рис. II–16
Замечания по программированию
Так как для функции определены два результата, один из них (в данной реализации количество разбиений, тип int) возвращается как результат функции (с помощьюreturn), а второй (массив указателей) должен быть передан с помощью соответствующего параметра функции. Так как рассматриваемая функция создает соответствующий массив динамически и возвращает его адрес, чтобы этот адрес стал известен за пределами функции, нужно передать в функцию либо указатель на поле, в которое должен быть записан полученный адрес, либо ссылку на это поле. Адрес массива определяется как указатель на (первый) элемент массива. Элемент массива определяет адрес фрагмента строки, т.е. имеет тип «указатель на (первый) символ строки». Следовательно, адрес массива должен быть размещен в элементе, тип которого – указатель на указатель на символ строки, т.е.char **.
Следовательно, если параметр функции, определяющий данный результат, определить как указатель, прототип функции будет иметь следующий вид:
int split(char *string, char fs, char ***mas);
В этом случае использование данной функции должно быть следующим:
. . .
char str[160], /* исходная строка */
smb, /* символ – разделитель */
**mas; /* массив указателей на выделенные фрагменты строки */
int n;
. . .
n = split(str, smb, &mas); /* вызов функции /*
Если же этот параметр определить как ссылку (что является естественным для программирования на С++ и невозможно в языке С), тогда прототип функции и ее использование будут следующими:
int split(char *string, char fs, char **&mas); /* прототип */
. . .
char str[160], /* исходная строка */
smb, /* символ – разделитель */
**mas; /* массив указателей на выделенные фрагменты строки */
int n;
. . .
n = split(str, smb, mas); /* вызов функции /*
Соответственно и в функции split()использование параметраmasбудет различным.
Замечу, что использование ссылки в программах на языке С++ является более естественным и обычно не вызывает никаких затруднений, тогда как использование указателей требует дополнительных усилий со стороны программиста. В дальнейшем мы будем использовать соответствующие возможности, предоставляемые языком С++.
Так как функция split()динамически выделяет память под выделенные фрагменты исходной строки и под массив указателей на эти фрагменты, приложение, использующее эту функцию, должно выделенную память освободить. Для корректной работы приложения важно знать, какими средствами выделяется память. Если память выделяется с помощью стандартной библиотечной функцииmalloc(), тогда освобождаться эта память должна с помощью библиотечной же функцииfree(). Если же для работы с памятью используются операторыnewиdeleteязыка С++, тогда библиотечные функцииmalloc()иfree()не должны использоваться. Несогласованное использование разных механизмов работы с памятью приводит к сбоям в работе приложения.
В наших примерах будут использоваться средства C++ для работы с памятью – операторыnewдля выделения памяти иdeleteдля освобождения; использование этих операторов, в целом ряде случаев, упрощает работу с динамической памятью.
Текст функции приводится ниже и в файле stringv.cpp.
Сначала приведу вспомогательную функцию, которая создает копию строки.
char * dupl(char *str)
{
char *res = new char[strlen(str) + 1];
strcpy(res, str);
return res;
}
Теперь рассмотрим функцию разбиения строки.
int split(char *string, char fs, char **&a)
{
int i, cnt;
char *ptr, *tmp ;
/* создадим копию исходной строки, чтобы не испортить ее */
tmp = dupl(string);
/* прежде всего, надо определить количество разбиений строки*/
for(ptr = tmp, cnt = 1; *ptr; ptr++)
if(*ptr == fs){
*ptr = '\0';
/* возможно, символ-разделитель строк не является последним символом в строке;
тогда после него размещается еще один фрагмент */
if(*(ptr + 1))
cnt++;
}
/* теперь выделим необходимую память под указатели на фрагменты строк*/
a = new char * [cnt];
/* наконец, формируем указатели на фрагменты строк */
for(i = 0, ptr = tmp; i < cnt; i++){
a[i] = dupl(ptr);
while(*ptr++)
;
}
delete [] tmp;
return cnt;
}
