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

LS-Sb87108

.pdf
Скачиваний:
36
Добавлен:
13.02.2021
Размер:
393.97 Кб
Скачать

МИНОБРНАУКИ РОССИИ

-------------

Санкт-Петербургский государственный электротехнический университет «ЛЭТИ»

------------------------------------------------------

В. А. КАЛМЫЧКОВ

ИСПОЛЬЗОВАНИЕ СТРУКТУРИРОВАННЫХ ДАННЫХ П Р И П Р О Г Р А М М И Р О В А Н И И Н А Я З Ы К Е С++

Учебное пособие

Санкт-Петербург Издательство СПбГЭТУ «ЛЭТИ»

2011

1

УДК 681.3 ББК 32.973

К17

Калмычков В. А. Использование структурированных данных при проК17 граммировании на языке С++: Учеб. пособие. СПб.: Изд-во СПбГЭТУ

«ЛЭТИ», 2011. 48 с.

ISBN 978-5-7629-1129-0

Содержит основные сведения, приемы и упражнения по программированию типовых схем решения задач с использованием структурированных данных для представления и обработки текстовой информации. Приведены краткие сведения по структурам и классам языка С++ и разобраны примеры их применения. Практические примеры и задания ориентированы на использование при проведении лабораторных и практических занятий, а также при выполнении курсовой работы по дисциплине «Программирование. Дополнительные главы» курса обучения программированию.

Предназначено для бакалавров направлений 230100.62 «Информатика и вычислительная техника», 231000.62 «Программная инженерия», 010400.62 «Прикладная математика и информатика», а также направлений подготовки в области информационных и компьютерных технологий для студентов заочной формы обучения.

УДК 681.3 ББК 32.973

Рецензенты: кафедра «Информационные системы и компьютерные технологии» Балтийского государственного технического университета «ВОЕНМЕХ» им. Д. Ф. Устинова; д-р техн. наук, проф. Л. Е. Шахмейстер (ФГУП НИИ «Поиск»).

Утверждено редакционно-издательским советом университета

в качестве учебного пособия

ISBN 978-5-7629-1129-0

СПбГЭТУ «ЛЭТИ», 2011

2

Введение

Учебное пособие содержит вспомогательные учебно-методические материалы по изучаемой студентами ФКТИ дисциплине «Программирование» и является дополнением к лекционному курсу, определяющим основную тематику практических занятий и направленность выполняемых во 2-й части курса лабораторных работ.

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

Формирование базовых приемов программирования на языке С++ (как, впрочем, и на любом другом языке программирования) невозможно без освоения основных принципов хранения и обработки в программах структурированных данных, обеспечивающих удобное и связанное представление необходимой для решения поставленной задачи информации. В определенной мере возможности по структуризации данных должны естественным образом основываться на синтаксисе выбранного языка программирования и предоставляемых синтаксических конструкциях. Задачей учебного пособия является рассмотрение общих подходов по представлению в программе текстовой информации и способов ее обработки, а также по обслуживанию простых динамических конструктивных образований с регулярной структурой на примере списков разного вида.

Рассматриваемые в учебном пособии варианты решений и используемые в них конструкции в целом не связаны с особенностями синтаксиса языка программирования С++ [1]. Представленные в качестве примеров программы могут быть реализованы и с применением других языков программирования. Сформулированные рекомендации и методы структурного представления информации необходимо использовать для выполнения заданий, приведенных в [2] и [3]. Требования к оформлению результатов представлены в [4].

В учебном пособии приведены примеры и рекомендации по решению типовых задач с использованием структурированных типов языка С++, а также описания возможностей по организации структурированных данных. Выбор варианта представления таких данных при решении конкретной задачи и методов их обработки всегда остается за студентом. При выполнении

3

заданий следует придерживаться представленных в пособии рекомендаций и требований по применяемым методам и использованию синтаксических конструкций, что не исключает привлечения иных способов достижения необходимого результата, представленных в рекомендуемой литературе и других изданиях по программированию [5]–[8] и языку С++ [1], [9]–[15].

1. ОБЩИЕ СВЕДЕНИЯ О СТРУКТУРИРОВАННЫХ ТИПАХ

Помимо встроенных (базовых, простых) типов данных язык программирования предоставляет способы для организации в программе сложных

(структурированных) типов. Структурированными (составными, компози-

ционными) типами называются такие способы представления данных, при которых их составными частями являются другие структурные элементы простых или также композиционных типов. Структуризация обеспечивается соответствующими синтаксическими конструкциями и правилами их оформления средствами языка программирования, а также дополнительными правилами упорядочения.

Между элементами таких структурированных типов может отсутствовать смысловая зависимость и любая связь, что характерно, например, для изученных ранее массивов [3], для которых составной принцип формирования базируется на едином типе элементов и смежных областях их размещения. В данном издании интерес представляют образования (структурные конструкции, списки) на основе разнотипных элементов. Кроме того, эти синтаксические образования могут обладать и способностью к модификации на основе изменения количества включаемых в них элементов или даже связей между ними, что придает программе динамический характер поведения представленных в ней данных.

Для структурированных данных основными операциями являются действия по доступу к элементам и модификации значений. При использовании в программе динамических образований добавляются операции для создания и удаления отдельных элементов. Методы доступа к элементам достаточно важны для структурированных данных, что может найти отражение в синтаксическом способе, предлагаемом языком программирования для реализации.

В синтаксис языка С для создания пользовательских типов была введена структура (struct), которая как синтаксическая единица языка программирования обеспечивала композицию в единое целое отдельных переменных (по-

4

лей-элементов структуры), типы которых могут быть разными. Структуры в С++ используются для логической или физической группировки данных, имеющих общий контекст. Это позволяло объединить разнотипные данные, относящиеся к представлению различных характеристик предметной области. Кроме того, была обеспечена более эффективная передача параметров в функции, предназначенные для автономной и/или совместной обработки полей структуры.

Рассмотрение предметной области решаемой задачи с точки зрения выделения в ней взаимодействующих объектов и отношений между ними с группировкой их в наборы и иерархии, объединенного представления свойств (данных) объектов и действий (методов), направленных на обработку этих данных, привело к включению в синтаксис языка С++ базового понятия класса объектов. Класс – это целенаправленное развитие понятия структуры. С их помощью можно формировать в программе новые типы и определять функции, манипулирующие с этими типами. Именно для более четкого выражения особенностей использования структурированных данных в язык С++ изначально и были включены классы, синтаксическое обслуживание которых позволило сформировать и развить принципиально иной подход к описанию и реализации обработки данных (членов класса) через предназначенные для этого методы (члены класса). Оставшиеся для сохранения преемственности синтаксиса в языке С++ структуры имеют расширенные синтаксические правила оформления, аналогичные классам, но могут иметь и более узкое, первоначально сформулированное для языка С, использование для композиционного представления данных. В учебном пособии структуры применяются именно в этом смысле, а классы привлекаются для демонстрации возможности синтаксического оформления композиционных данных и программной реализации действий с ними. Более детально особенности использования классов рассматриваются в рамках изучения других дисциплин.

Обращение к полям структуры (членам класса) осуществляется по их именам двумя способами посредством операторов доступа – прямого к полю

(.) и через указатель (->). Оператор точка (.) используется для имени переменной (объекта) или соответствующей ему ссылки. Оператор для работы через указатель (->) реализуется для указателей на объект. Например, если имя поля (члена) есть M, то для указателя q обращение к M может быть записано (*q).M, что эквивалентно q->M.

5

2.ПРЕДСТАВЛЕНИЕ И ОБРАБОТКА СТРОК

2.1.Понятие строки

Строку следует рассматривать как линейную последовательность символов, принадлежащих конечному множеству, называемому алфавитом. Строка в С++ обычно рассматривается как последовательность символов (байт), завершающаяся специальным символом – '\0'. Этот символ формируется автоматически в случае задания строки в виде текстовой константы в формате "sssssssss" (s – произвольный символ алфавита). В памяти символ располагается непосредственно за всеми отличными от него символами.

Обычно алфавит является фиксированным, а длина строки может быть произвольной, но конечной. Доступ к строке может быть организован таким образом, чтобы обработке подлежал отдельный ее элемент или некая подпоследовательность символов. Обычно рассмотрению подлежат текстовые строки, т. е. строки из символов, включающих буквы, цифры, знаки препинания и отдельные служебные символы.

При использовании в программах текстовых строк обычно должны быть обеспечены следующие основные операции:

определение длины строки;

проверка содержимого строки на соответствие некоторому признаку;

поиск символа или выделение подстроки в строке;

изменение содержимого строки вставкой или удалением символа или подстроки;

обеспечение взаимодействия строк – сравнение, сцепление, подстановка и др.;

комбинирование операций (например, заменить часть строки на другое содержимое можно первоначальным удалением подстроки и последующей вставкой нового фрагмента).

Следует отметить основную проблему, которую необходимо решать при написании программ по обработке текстовых строк. В случае выполнения любых преобразований по вставке и удалению над содержимым строк требуется четко контролировать реальную длину строки по отношению к физически выделенной. Отсутствие планомерного отслеживания всех операций над строками может приводить к возникновению трудноопределяемых ошибок в момент исполнения программы или ее аварийному завершению. При этом следствие ошибки может оказаться столь отдаленным от места ее возникновения, что элементарное изучение текста программы или ее пошаговое вы-

6

полнение не позволит сразу выявить причину происходящего и даже явно высказать правильное предположение о сути ошибочных действий.

2.2. Основные варианты представления строк

Имеется несколько подходов к логическому представлению строк на основе существующих в соответствующем языке программирования синтаксических конструкций. Обычно все сводится к вариациям двух основных – на основе использования массива (статичного или динамического) и линейного списка. При разработке программы могут быть выбраны любые из них, включая комбинированные. Для языка С++ дополнительно созданы библиотеки, обеспечивающие представление, хранение и расширенный набор операций по обработке текстовых строк.

Выбор представления строк в памяти в основном определяется решаемыми задачами в зависимости или от необходимости только хранения строки или выполнения многочисленных ее изменений. При этом непосредственное выделение памяти под размещение содержимого строки может быть как статичным, так и динамическим. Традиционно предусматривается возможность работы со строками переменной длины, но при этом обычно фигурирует максимальная длина строки, задаваемая при выделении для нее памяти. При необходимости эффективного использования памяти или обеспечения работы со строкой произвольной длины в программе используются различные варианты оперирования с динамической памятью.

2.2.1. Векторное представление строки на основе массива

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

Пример 1. Рассмотрим фрагмент возможной реализации этого варианта и обозначенных возможностей по заполнению массива из 10 элементов желаемыми строками «54321» и «abcdefghijklm» с нейтральным пробелом ( ̺):

7

5 4 3 2 1 ̺ ̺ ̺ ̺ ̺ a b c d e f g h i j const unsigned N = 10; unsigned i; const char * a1 = "54321", * a2 = "abcdefghijklm";

char ArrayStr[N] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};

for( i = 0; i < N; i++ ) // будет записано только 5 символов

{if(*(a1 + i) == '\0') break; ArrayStr[i] = *(a1 + i);} for( i = 0; i < N; i++ )

cout << ArrayStr[i]; //будет выведено N символов, из которых только 5 первых – значимые for( i = 0; i < N; i++ ) ArrayStr[i] = ' ';

i = 0;

while( i < N ) // будут скопированы только первые N символов – abcdefghij {if(*(a2 + i) == '\0') break; ArrayStr[i] = *(a2 + i); i++;}

for( i = 0; i < N; i++)

cout << ArrayStr[i]; // будет выведено N символов, из которых все значимые

2.2.2. Представление строки с маркером с использованием массива

Маркер – это выбранный нейтральным символ, принадлежащий алфавиту (используемый алфавит уменьшается на один символ), которым обязательно заканчивается информационное содержимое строки. Маркер занимает в массиве столько же элементов, сколько и любой символ строки. В этом случае необходимо при выделении памяти под массив дополнительно резервировать 1 элемент под маркер.

В языке С++ в качестве маркера используется символ, имеющий код 0. Однако при реализации представления строки в качестве маркера может быть выбран любой другой символ, значение которого необходимо хранить отдельно от строки. Перебор символов строки осуществляется с ее начала до встречи в массиве маркера. Для совместного хранения строки и такого маркера удобно использовать структуру или класс.

Пример 2. Рассмотрим фрагменты вывода информационного содержимого строки при возможных реализациях этого варианта с символом '@' в ка-

честве маркера:

 

 

 

 

 

 

 

 

 

 

 

 

 

– это содержимое Str

 

й

ц

у

к

е

н

@

?

?

 

 

 

 

 

 

 

 

 

 

 

 

– это значение Mark

 

@

 

 

 

 

 

 

 

 

 

const unsigned N = 8; unsigned i;

const unsigned int N = 8; unsigned i;

struct StrMark

class StrMark

{char Str[N + 1]; // информационное содержимое

{char Str[N + 1]; char Mark;

char Mark; // маркер

 

public:

};

 

 

 

 

 

 

 

 

 

 

void setMark(char s) {Mark = s;}

 

 

 

 

 

 

 

 

 

 

 

char getMark() {return Mark;}

 

 

 

 

 

 

 

 

 

 

 

 

8

 

void setStr(unsigned i, char s) {Str[i] = s;}

char getStr(unsigned i) {return Str[i];}

 

};

StrMark Stroka;

StrMark Stroka; char v;

for(i = 0; i < N - 2; i++) // чтениеN - 2 символов

for( i = 0; i < N - 2; i++ )

cin >> Stroka.Str[i]; // например, йцукен

{cin >> v; Stroka.setStr(i, v);}

Stroka.Mark = '@';

Stroka.setMark('@');

Stroka[N - 2] = Stroka.Mark;

Stroka.setStr(N - 2, Stroka.getMark());

i = 0;

i = 0;

while(Stroka.Str[i] != Stroka.Mark)

while(Stroka.getStr(i) != Stroka.getMark())

{cout << Stroka.Str[i]; i++;}

{cout << Stroka.getStr(i); i++;}

 

 

2.2.3. Представление строки с длиной с использованием массива

Длина – это неотрицательное целое число, которое хранит количество символов строки, составляющих ее информационное содержимое. Для длины должен быть выбран такой тип, чтобы его диапазон значений обеспечивал представление размера выделенной для строки памяти.

При использовании представления с длиной обеспечивается произвольный доступ к символам информационного содержимого строки, при этом можно контролировать соответствие индекса запрашиваемого символа и предельного размера строки. Перебор элементов строки можно начинать с ее начала или конца в зависимости от целей просмотра и обработки. Для совместного хранения строки идлины удобно использовать структуруили класс.

Пример 3. Рассмотрим фрагменты вывода информационного содержимого строки при возможных реализациях этого варианта:

 

й

 

ц

у

к

е

н

?

?

 

 

– это содержимое Str

 

 

 

 

 

 

 

 

 

 

 

 

– это значение Len

 

 

6

 

 

 

 

 

 

 

 

const unsigned

 

unsigned i;

 

 

const unsigned int N = 8; unsigned i;

N = 8;

 

struct StrLen

 

 

 

 

 

 

 

 

 

 

class StrLen

{char Str[N]; // информационное содержимое

 

 

{char Str[N]; unsigned Len;

unsigned Len;

// длина

 

 

public:

};

 

 

 

 

 

 

 

 

 

 

 

void setLen(unsigned z) {Len = z;}

 

 

 

 

 

 

 

 

 

 

 

 

unsigned getLen() {return Len;}

 

 

 

 

 

 

 

 

 

 

 

 

void setStr(unsigned i, char s) {Str[i] = s;}

 

 

 

 

 

 

 

 

 

 

char getStr(unsigned i) {return Str[i];}

 

 

 

 

 

 

 

 

 

 

 

 

};

StrLen Stroka;

 

 

 

 

 

 

 

 

 

 

StrLen Stroka; char v;

for(i = 0; i < N - 2; i++) // чтениеN - 2 символов

 

 

for( i = 0; i < N - 2; i++ )

cin >> Stroka.Str[i];

// например, йцукен

 

 

{cin >> v; Stroka.setStr(i, v);}

 

 

 

 

 

 

 

 

 

 

 

 

 

9

StrDynLen Stroka; unsigned i;
cin >> Stroka.Len; //вводчисласимволоввстроке
Stroka.p = new char [Stroka.Len];
if(Stroka.p != NULL)
{for(i = 0; i < Stroka.Len; i++) // чтениесимволов cin >> Stroka.p[i]; // например, йцукен
for(i = 0; i < Stroka.Len; i++) cout << Stroka.p[i]; }
10
class StrDynLen {unsigned Len; char* p;
public: char* getp() {return p;} StrDynLen(unsigned z)
{Len = z; p = new char [Len];} ~StrDynLen()
{if(p != NULL) delete [ ] p; p = NULL;}
unsigned getLen() {return Len;}
void setStr(unsigned i, char s) {p[i] = s;} char getStr(unsigned i) {return p[i];}
};
StrDynLen* pStroka; char v; unsigned i; cin >> i;
pStroka = new StrDynLen(i); if(pStroka != NULL) {if(pStroka.getp() != NULL)
{for( i = 0; i < pStroka->getLen(); i++ ) {cin >> v; pStroka->setStr(i, v);} for(i = 0; i < pStroka->getLen(); i++) cout << pStroka->getStr(i); }
struct StrDynLen {unsigned Len; // длина
char* p; // информационное содержимое
};
pStroka
Stroka
– указатель p
указатель p –
й ц у к е н

Stroka.Len = N - 2;

Stroka.setLen(N - 2);

for(i = 0; i < Stroka.Len; i++)

for(i = 0; i < Stroka.getLen(); i++)

cout << Stroka.Str[i];

cout << Stroka.getStr(i);

 

 

2.2.4. Представление строки с длиной на основе динамического массива

Недостатком рассмотренных ранее вариантов является то, что для хранения строки выделяется память заранее предопределенного размера, причем часть ее может не использоваться вообще или, наоборот, ее размера не хватает для размещения всего информационного содержимого строки.

При использовании представления с длиной (этот вариант адаптируем и на представление с маркером) можно динамически выделить память для массива под реальное наполнение строки содержимым.

Пример 4. Рассмотрим фрагменты вывода информационного содержимого строки при возможных реализациях этого варианта:

значение Len –

6

 

Информационное

 

6

– значение Len

 

 

 

 

содержимое

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]