- •Предисловие
- •Глава 1. Основные понятия
- •1.1. Элементы языка программирования
- •1.2. Процесс создания программы
- •1.3. Первая программа
- •1.4. Состав программы
- •Глава 2. Средства разработки на C++
- •2.1. Системы Turbo C++ 3.0/Borland C++ 3.1
- •2.2. Система C++ Builder
- •Глава 3. Работа с числовыми данными
- •3.1. Целые типы
- •3.2. Числа с плавающей точкой
- •3.3. Ввод и вывод чисел
- •3.4. Логический тип и логические операции
- •3.5. Математические функции
- •Глава 4. Операторы. Ключевые слова
- •4.1. Операторы
- •4.2. Приоритеты операторов
- •4.3. Ключевые слова
- •4.4. Структура программы
- •4.5. Константы
- •Задачи - . Простейшие вычисления
- •Глава 5. Управление и циклы
- •5.1. Условный оператор
- •5.2. Операторы цикла
- •5.3. Переключатель
- •5.4. Операторы break и continue
- •Задачи -. Выбор и циклы
- •Глава 6. Массивы
- •6.1. Одномерные массивы
- •6.2. Двумерные массивы
- •Задачи -. Одно- и двумерные массивы
- •Глава 7. Функции
- •7.1. Определение функции
- •7.2. Формальные параметры и фактические аргументы
- •7.3. Автоматические и статические переменные
- •7.4. Прототипы функций
- •7.5. Массивы как аргументы функций
- •7.6. Внешние переменные
- •7.7. Рекурсия
- •7.8. Перегруженные имена функций
- •7.9. Аргументы функций по умолчанию
- •Задачи -. Функции
- •Глава 8. Символы и строки
- •8.1. Символы
- •8.2. Строки символов
- •Задачи -. Символы и строки
- •Глава 9. Препроцессор
- •9.1. Директивы препроцессора
- •9.2. Макросы
- •Задачи -. Макросы
- •Глава 10. Указатели и ссылки
- •10.1. Указатели и адреса
- •10.2. Указатели и массивы
- •10.3. Адресная арифметика
- •10.4. Символьные указатели
- •10.5. Массивы указателей
- •10.6. Указатели на функции
- •10.7. Ссылки
- •10.8. Операторы new и delete
- •Задачи -. Указатели и ссылки
- •Глава 11. О файлах и командной строке
- •11.1. Знакомство с файлами
- •11.2. Командная строка
- •11.3. Перенаправление стандартного ввода и вывода на файл
- •11.4. Аргументы командной строки
- •Задачи -. Файлы и командная строка
- •Глава 12. Работа с экраном дисплея
- •12.1. Текстовый режим
- •12.2. Графический режим
- •Задачи -. Работа с экраном
- •Глава 13. Внутреннее представление чисел
- •13.1. Двоичная система счисления
- •13.2. Беззнаковые целые
- •13.3. Двоичный дополнительный код
- •13.4. Двоичный код с избытком
- •13.5. Побитовые операторы
- •13.6. Дробные числа в двоичной системе
- •13.7. Внутреннее представление плавающих типов
- •13.8. Преобразование типов
- •Задачи -. Побитовые операторы
- •Глава 14. Структуры, перечисления, объединения
- •14.1. Объявление структур
- •14.2. Структуры и функции
- •14.3. Указатели на структуры
- •14.4. Массивы структур
- •14.5. Перечисления
- •14.6. Объединения
- •14.7. Битовые поля
- •14.8. О бинарных файлах
- •Задачи -. Структуры
- •Глава 15. Классы
- •15.1. Структуры в C++. Инкапсуляция
- •15.2. Встроенные функции
- •15.3. Классы. Скрытие данных
- •15.4. Конструкторы
- •15.5. Статические члены класса
- •15.6. Друзья класса
- •15.7. Копирование объектов класса
- •15.8. Управление доступом
- •15.9. Ссылка на себя
- •15.10. Деструкторы
- •Задачи -. Работа с классами
- •Глава 16. Программы из нескольких файлов
- •16.1. Работа с проектами
- •16.2. Область действия имен
- •16.3. Заголовочные файлы
- •16.4. Пространства имен
- •Задачи -. Работа со стеком
- •Глава 17. Перегрузка операторов
- •17.1. Правила перегрузки операторов
- •Задачи -. Перегрузка операторов
- •Глава 18. Конструктор копирования и оператор присваивания
- •18.1. Проблемы при копировании
- •Задачи -. Конструктор копирования
- •Глава 19. Ввод и вывод
- •19.1. Вывод
- •19.2. Ввод
- •19.3. Ввод и вывод определяемых пользователем типов
- •19.4. Работа с файлами
- •Глава 20. Взаимоотношения классов
- •20.1. Объекты как члены класса
- •20.2. Конструкторы встроенных типов
- •20.3. Наследование
- •20.4. Виртуальные функции
- •20.5. Абстрактные классы
- •20.6. Совместимость типов
- •20.7. Множественное наследование
- •Задачи -. Наследование классов
- •Глава 21. Шаблоны, исключения
- •21.1. Шаблоны
- •21.2. Шаблоны функций
- •21.3. Классы и шаблоны
- •21.4. Обработка исключений
- •21.5. Стандартная библиотека шаблонов
- •Литература
- •Предметный указатель
184 14
Рис.66. Прямоугольник на экране, созданный программой
14.4. Массивы структур
Структуры, как и любые другие переменные, можно объединять в массивы. Работу с массивами структур разберем на примере.
Напишем программу, которая читает входной поток и определяет число вхождений каждого ключевого слова языка Си в читаемый текст. Для хранения ключевых слов и счетчиков можно было бы создать два параллельных массива:
char *keyword[NKEYS]; int keycount[NKEYS];
Однако для более удобной работы лучше создать массив структур.
Программа 37. Подсчет ключевых слов
Для работы с ключевыми словами и их счетчиками определим следующий массив структур:
// Файл KeyWords.cpp |
|
|
|
struct key{ |
|
|
// Структура key включает два поля: |
char* word; |
|
// word – указатель на ключевое слово, |
|
int count; |
|
|
// count - число его вхождений в текст |
} keytab[] = { |
|
// keytab - массив из структур key |
|
"auto", |
0, "break", |
0, |
// Массив структур инициализируется |
"case", |
0, "char", |
0, |
// списком ключевых слов и нулями для |
"const", |
0, "continue",0, |
// чисел вхождений. |
|
"default", |
0, "do", |
0, |
// Отметим, что ключевые слова |
"double", |
0, "else", |
0, |
// упорядочены в алфавитном порядке. |
"enum", |
0, "extern", |
0, |
|
"float", |
0, "for", |
0, |
|
"goto", |
0, "if", |
0, |
|
"int", |
0, "long", |
0, |
|
"register", |
0, "return", |
0, |
|
"short", |
0, "signed", |
0, |
|
"sizeof", |
0, "static", |
0, |
|
"struct", |
0, "switch", |
0, |
|
"typedef", |
0, "union", |
0, |
|
"unsigned",0, "void", |
0, |
|
|
"volatile", |
0, "while", |
0 |
|
}; |
|
|
|
Объявление структуры key используется как тип элементов массива keytab. Размер массива определяется по списку инициализации,
Структуры, перечисления, объединения 185
который, строго говоря, следовало бы задавать для каждой структуры из массива в виде:
{{"auto", 0}, {"break", 0},…};
но поскольку имеется правильное чередование строк и чисел, внутренние фигурные скобки не обязательны. Ключевые слова перечисляются в алфавитном порядке, чтобы создать упорядоченный массив, с которым проще работать. Важно помнить, что сами ключевые слова, заданные строковыми константами, хранятся где-то в памяти, в структурах же key хранятся только указатели на эти строки.
//Продолжение файла KeyWords.cpp
//NKEYS - количество элементов массива keytab int NKEYS = (sizeof keytab / sizeof(key));
Оператор sizeof может выполняться на этапе компиляции. Выражение sizeof keytab равно размеру массива keytab в байтах, выражение sizeof(key) дает размер одной структуры, отношение этих выражений есть количество элементов массива.
Предусмотрим в программе функцию fgetword для чтения из файла очередного слова и функцию binsearch для поиска ключевого слова в массиве ключевых слов.
//Продолжение файла KeyWords.cpp #include <iostream.h>
#include <fstream.h> #include <string.h> #include <stdlib.h> #include <conio.h> #include <ctype.h>
//fgetword: читает очередное слово int fgetword(ifstream& , char*, int);
//binsearch: бинарный поиск в упорядоченном массиве int binsearch(char*, key*, int);
Перед тем как привести текст функции main, опишем ее работу. Программа предлагает указать имя файла, из которого следует прочитать текст, открывает файл на чтение и читает содержимое файла по одному слову. Словом считается последовательность букв и цифр, начинающаяся с латинской буквы, а также любой отдельный символ. Разделителями слов считаются пробельные символы. Каждое прочитанное слово ищется в массиве keytab с помощью функции binsearch. Если слово будет найдено в массиве, то есть является ключевым, увеличивается соответствующий счетчик. Ключевые слова, найденные в тексте, печатаются вместе со значениямия счетчиков.
186 14
Далее программа просит ввести имя выходного файла, который сначала открывается на чтение. Если выходной файл удается открыть на чтение, значит файл уже существует. В этом случае программа выдает запрос на подтверждение записи в существующий файл нового содержимого. Если пользователь программы дает положительный ответ, выходной файл открывается на запись, после чего в него выводятся найденные ключевые слова и их счетчики.
// Продолжение файла KeyWords.cpp
const int MAXLENWORD = 100; |
// Максимальная длина слова |
|
// Подсчет ключевых слов Си |
|
|
void main() |
|
|
{ |
|
|
int n; |
|
|
char word[MAXLENWORD], c; |
|
|
const int LENFN = 30; |
|
// Длина имен файлов |
char inpfname[LENFN], |
|
// Имя входного |
outfname[LENFN]; |
|
// и выходного файла |
ifstream inpf; |
|
// Входной и |
ofstream outf; |
|
// выходной файловые потоки |
ifstream tmpf; |
|
// Вспомогательный файловый поток |
cout << "\nВведите имя входного файла\n"; |
||
cin.getline(inpfname, LENFN); |
// Ввод строки с именем файла |
|
inpf.open(inpfname); |
|
// Открытие файла на чтение |
if(!inpf){ |
|
// Если не удалось открыть файл |
cerr << "Не могу открыть файл " << inpfname; |
||
exit(1); |
|
// Завершение программы |
} |
|
|
while(fgetword(inpf, word, MAXLENWORD) != EOF) // Пока есть слова |
||
if(isalpha(word[0])) |
// Если слово начинается с латинской буквы |
|
if((n = binsearch(word, keytab, NKEYS)) >= 0) // и ключевое, |
||
keytab[n].count++; |
|
// увеличиваем счетчик |
for(n = 0; n < NKEYS; n++) |
// Вывод на экран ключевых слов, |
|
if(keytab[n].count > 0) |
|
// найденных в тексте |
cout << keytab[n].count << ' ' << keytab[n].word << endl; cout << "Введите имя выходного файла\n";
cin.getline(outfname, LENFN); |
// Ввод имени выходного файла |
|
tmpf.open(outfname); |
// Попытка открыть выходной файл на чтение |
|
if(!tmpf == 0){ |
// Если удалось, то файл существующет |
|
cout << "Файл " << outfname << " существует. Переписать? Y/N"; |
||
if((c = cin.get()) != 'Y' && c != 'y') |
// Если не подтверждаем, |
|
exit(0); |
|
// завершение программы |
} |
|
|
tmpf.close(); |
// Закрываем временный файловый поток |
|
outf.open(outfname); |
// Открываем файл на запись |
Структуры, перечисления, объединения 187
for(n = 0; n < NKEYS; n++) // Запись в файл if(keytab[n].count > 0)
outf << keytab[n].count << ' ' << keytab[n].word <<endl; getch();
}
Для поиска слова в упорядоченном по возрастанию массиве ключевых слов применен метод бинарного поиска, который состоит в следующем. Искомый элемент сравнивается со срединным элементом массива. Если искомый элемент меньше срединного, дальнейший поиск производится в первой половине массива, если больше, то во второй половине массива. При совпадении поиск заканчивается и возвращается номер найденного элемента массива. После каждого шага поиска диапазон поиска сужается в два раза. Отсюда происходит название метода. Если нижняя граница поиска превысит верхнюю, поиск прекращается как неудачный и в качестве результата возвращается –1.
//Продолжение файла KeyWords.cpp
//binsearch: найти слово в tab[0]...tab[n-1] int binsearch(char* word, key v[], int n)
{
int cond; |
|
int low, high, mid; |
|
low = 0; |
// Нижняя граница поиска |
high = n - 1; |
// Верхняя граница поиска |
while(low <= high){ |
|
mid = (low + high) / 2; |
// Номер срединного элемента массива |
// strcmp: стандартная функция сравнения строк if((cond = strcmp(word, v[mid].word)) < 0)
high = mid - 1; else if(cond > 0) low = mid + 1;
else
return mid;
}
return -1;
}
/* fgetword: принимает следующее слово или символ из файла. Возвращает первый символ слова. Слово – последовательность символов, начинающаяся с буквы или одиночный символ,
не являющийся буквой */
int fgetword(ifstream& f, char* word, int lim)
{
int c;
char* w = word;
188 |
14 |
|
while(isspace(c = f.get())) |
// Пропуск пробельных |
|
; |
|
// символов в начале слова |
if(c != EOF) |
|
|
*w++ = c; |
|
|
if(! isalpha(c)){ |
// Если первый символ не буква, |
|
* w = '\0'; |
// считаем ее отдельным словом |
|
return c; |
|
|
} |
|
|
for( ; --lim > 0; w++) |
// Цикл по слову |
|
if(!isalnum(*w = f.get())){ |
// Если поступила не буква и не цифра, |
|
|
f.putback(*w); |
// возвращаем символ обратно во ввод |
} |
break; |
// и выходим из цикла |
|
|
|
*w = '\0'; |
// Признак конца строки |
|
return word[0]; |
// Возвращаем первый символ |
|
} |
|
|
Далее приведены диалоги с программой для двух запусков. В качестве входного файла в обоих случаях был указан файл с исходным текстом программы 34 Star.cpp. При первом запуске в качестве имени выходного файла было указано имя существующего файла. Программа это обнаружила и задала соответствующий вопрос.
Введите имя входного файла Star.cpp
3 const
2double
2for
1if 10 int
1return
Введите имя выходного файла Star.cpp
Файл Star.cpp существует. Переписать? Y/NN
Введите имя входного файла Star.cpp
3 const
2double
2for
1if 10 int
1return
Введите имя выходного файла Result36.txt