- •Кетков ю.Л.
- •Раздел 5. Системные данные текстового типа 33
- •Раздел 6. Основные синтаксические конструкции языка c 46
- •Раздел 7. Указатели и ссылки 59
- •Раздел 8. Функции и их аргументы 62
- •Раздел 9. Работа с массивами. 74
- •Раздел 10. Пользовательские типы данных. 95
- •Раздел 11. Работа с файлами 104
- •Раздел 12. Библиотеки стандартных и нестандартных функций 118
- •Раздел 15. Классы. Создание новых типов данных 131
- •Раздел 16. Классы как средство создания больших программных комплексов 150
- •Раздел 17. Прерывания, события, обработка исключений 167
- •Введение
- •Раздел 1. Немного истории
- •Раздел 2. Структура программы на языке c
- •Раздел 3. Среда программирования
- •Раздел 4. Системные данные числового типа
- •4.1. Типы числовых данных и их представление в памяти эвм
- •4.1.1. Внутреннее представление целочисленных данных
- •4.1.2. Однобайтовые целочисленные данные
- •4.1.3. Двухбайтовые целочисленные данные
- •4.1.4. Четырехбайтовые целочисленные данные
- •4.1.5. Восьмибайтовые целочисленные данные
- •4.2. Внутреннее представление данных вещественного типа
- •4.3. Внешнее представление числовых констант
- •4.4. Объявление и инициализация числовых переменных
- •4.5. Ввод числовых данных по запросу программы
- •4.5.1. Потоковый ввод данных числового типа
- •4.5.2. Форматный ввод
- •4.6. Вывод числовых результатов
- •4.6.1. Форматный вывод
- •4.6.2. Потоковый вывод
- •4.7. Примеры программ вывода числовых данных
- •4.8. Операции над числовыми данными целого типа
- •4.9. Операции над числовыми данными вещественного типа
- •Раздел 5. Системные данные текстового типа
- •5.1. Символьные данные и их представление в памяти эвм
- •5.2. Строковые данные и их представление в памяти эвм
- •5.3. Ввод текстовых данных во время работы программы
- •5.3.1. Форматный ввод
- •5.3.3. Потоковый ввод
- •5.3.4. Специальные функции ввода текстовых данных
- •5.4. Вывод текстовых данных
- •5.4.1. Форматный вывод
- •5.5.2. Операции над строковыми данными
- •5.6. Управление дисплеем в текстовом режиме
- •Раздел 6. Основные синтаксические конструкции языка c
- •6.1. Заголовок функции и прототип функции
- •6.2. Объявление локальных и внешних данных
- •6.3. Оператор присваивания
- •6.4. Специальные формы оператора присваивания
- •6.5. Условный оператор
- •6.6. Оператор безусловного перехода
- •6.7. Операторы цикла
- •6.8. Дополнительные операторы управления циклом
- •6.9. Оператор выбора (переключатель)
- •6.10. Обращения к функциям
- •6.11. Комментарии в программах
- •Раздел 7. Указатели и ссылки
- •7.1. Объявление указателей
- •7.2. Операции над указателями
- •7.3. Ссылки
- •Раздел 8. Функции и их аргументы
- •8.1. Параметры-значения
- •8.2. Параметры-указатели
- •8.3. Параметры-ссылки
- •8.4. Параметры-константы
- •8.5. Параметры по умолчанию
- •8.6. Функции с переменным количеством аргументов
- •8.7. Локальные, глобальные и статические переменные
- •8.8. Возврат значения функции
- •8.9. Рекурсивные функции
- •8.10. Указатели на функцию и передача их в качестве параметров
- •8.11. "Левые" функции
- •Раздел 9. Работа с массивами.
- •9.1. Объявление и инициализация массивов.
- •9.2. Некоторые приемы обработки числовых массивов
- •9.2. Программирование задач линейной алгебры
- •9.2.1. Работа с векторами
- •9.2.2.Работа с матрицами
- •9.3. Поиск
- •9.3.1. Последовательный поиск
- •9.3.2. Двоичный поиск
- •9.4. Сортировка массивов.
- •9.4.1. Сортировка методом пузырька
- •9.4.2. Сортировка методом отбора
- •9.4.3. Сортировка методом вставки
- •9.4.4. Сортировка методом Шелла
- •9.4.5.Быстрая сортировка
- •9.5. Слияние отсортированных массивов
- •9.6. Динамические массивы.
- •Раздел 10. Пользовательские типы данных.
- •10.1. Структуры
- •10.1.1. Объявление и инициализация структур
- •10.1.2. Структуры – параметры функций
- •10.1.3.Функции, возвращающие структуры
- •10.2. Перечисления
- •10.3. Объединения
- •Раздел 11. Работа с файлами
- •11.1.Файлы в операционной системе
- •11.1. Текстовые (строковые) файлы
- •11.2. Двоичные файлы
- •11.3. Структурированные файлы
- •11.4. Форматные преобразования в оперативной памяти
- •11.5. Файловые процедуры в системе bcb
- •11.5.1. Проверка существования файла
- •11.5.2. Создание нового файла
- •11.5.3. Открытие существующего файла
- •11.5.4. Чтение из открытого файла
- •11.5.5. Запись в открытый файл
- •11.5.6. Перемещение указателя файла
- •11.5.7. Закрытие файла
- •11.5.8. Расчленение полной спецификации файла
- •11.5.9. Удаление файлов и пустых каталогов
- •11.5.10. Создание каталога
- •11.5.11. Переименование файла
- •11.5.12. Изменение расширения
- •11.5.13. Опрос атрибутов файла
- •11.5.14. Установка атрибутов файла
- •11.5.15. Опрос и изменение текущего каталога
- •11.6. Поиск файлов в каталогах
- •Раздел 12. Библиотеки стандартных и нестандартных функций
- •12.2. Организация пользовательских библиотек
- •12.3. Динамически загружаемые библиотеки
- •13.1. Препроцессор и условная компиляция
- •13.2. Компилятор bcc.Exe
- •13.3. Утилита grep.Com поиска в текстовых файлах
- •14.1. Переопределение (перегрузка) функций
- •14.2. Шаблоны функций
- •Раздел 15. Классы. Создание новых типов данных
- •15.1. Школьные дроби на базе структур
- •15.2. Школьные дроби на базе классов
- •15.3. Класс на базе объединения
- •15.4. Новые типы данных на базе перечисления
- •15.5. Встраиваемые функции
- •15.6. Переопределение операций (резюме)
- •15.8. Конструкторы и деструкторы (резюме)
- •Раздел 16. Классы как средство создания больших программных комплексов
- •16.1. Базовый и производный классы
- •16.1.1.Простое наследование
- •16.1.2. Вызов конструкторов и деструкторов при наследовании
- •16.1.3. Динамическое создание и удаление объектов
- •16.1.4. Виртуальные функции
- •16.1.5. Виртуальные деструкторы
- •16.1.6. Чистые виртуальные функции и абстрактные классы
- •16.2. Множественное наследование и виртуальные классы
- •16.3. Объектно-ориентированный подход к созданию графической системы
- •Раздел 17. Прерывания, события, обработка исключений
- •17.1. Аппаратные и программные прерывания
- •17.2. Исключения
9.5. Слияние отсортированных массивов
Довольно много методов сортировки построено на сортировке фрагментов массивов с последующим объединением (слиянием) двух или более фрагментов в общий массив. Ниже приведена одна из реализаций объединения двух отсортированных массивов a и b, содержащих, соответственно, по ka и kb упорядоченных по возрастанию целых чисел. Чего-то особенного в алгоритме слияния нет – надо поочередно просматривать претендентов из обоих массивов и вставлять нужный из них в результирующий массив. Единственное, за чем приходится следить – не исчерпался ли тот или иной поставщик данных. Несмотря на использование в функции merge трех операторов goto, приведенный вариант представляет собой наиболее эффективную программу слияния.
#include <stdio.h>
#include <conio.h>
void merge(int *a,int ka,int *b, int kb, int *c)
{ int ja=0,jb=0,jc;
for(jc=0; jc<ka+kb; jc++)
{ if(ja==ka) goto mb;
if(jb==kb) goto ma;
if(a[ja]<b[jb]) goto ma;
mb: c[jc]=b[jb]; jb++; continue;
ma: c[jc]=a[ja]; ja++;
}
}
#define na 3
#define nb 4
void main()
{ int j,a[na]={0,2,4},b[nb]={1,3,5,7};
int c[na+nb];
for(j=0; j<na; j++) printf("%4d",a[j]);
printf("\n");
for(j=0; j<nb; j++) printf("%4d",b[j]);
printf("\n");
merge(a,na,b,nb,c);
for(j=0; j<na+nb; j++) printf("%4d",c[j]);
getch();
}
//=== Результат работы ===
0 2 4
1 3 5 7
0 1 2 3 4 5 7
9.6. Динамические массивы.
К динамическим массивам относятся массивы, память под которые выделяется работающей программе по запросам, предусмотренным программистом. Не следует путать их с локальными массивами функций, память под которые автоматически выделяется при обращении к функции и также автоматически возвращается при выходе из функции. Память, выделенная под динамические массивы, освобождается в результате вызова специальных процедур, которые должны быть предусмотрены в тексте программы.
Пусть q – указатель на одномерный массив с элементами типа type_q. Тогда запрос на выделение памяти без ее предварительной очистки выполняется с помощью функции malloc:
q=(type_q *)malloc(n_byte);
Приведение к типу данных потребовалось потому, что функция malloc возвращает указатель типа void. Аргументом функции malloc является запрашиваемое количество байт. Необходимо иметь в виду, что данные типа int в 16-битной системе программирования (например, BC 3.1 под управлением MS-DOS) занимают по 2 байта, а в 32-битной среде типа BCB – по 4 байта.
Аналогичный запрос на выделении памяти с ее предварительной очисткой выполняется с помощью функции calloc:
q=(type_q *)calloc(n_el,sizeof(type_q));
В отличие от предыдущей функции здесь уже два аргумента – количество элементов массива (n_el) и длина каждого элемента в байтах sizeof(type_q).
Прототипы обеих функций находятся в заголовочных файлах alloc.h и stdlib.h. Если по каким-то причинам память выделить не удалось, каждая из функций возвращает нулевой указатель (q==NULL).
После выделения блока памяти по malloc или calloc его можно перераспределить, изменив ранее объявленную длину:
q=(type_q)realloc(q,new_len);
Если новая длина больше предыдущей, то содержимое массива q копируется в начало нового блока памяти. Если новая длина меньше предыдущей, то в новом блоке сохраняются значения только начала старого массива. Если new_len=0, то это эквивалентно освобождению занимаемого блока памяти.
После того, как массив q будет использован и больше не понадобится, выделенную память надо возвратить с помощью функции free:
free(q);
Освобождение памяти не сбрасывает указатель q, поэтому с целью предупреждения возможных ошибок в дальнейшем его следует обнулить (операционная система Windows блокирует запись по нулевому адресу):
q=NULL; //или q=0;
Некоторое представление о работе описанных функций дает следующий пример:
#include <alloc.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
void main()
{ int j,*p,s;
char *str="abcdefghijk",*s1;
p=(int *)malloc(4000); //запрос "грязной" памяти
for(s=0,j=0; j<1000; j++) s += p[j];
printf("s=%d",s); //улика - память "грязная"
for(j=0; j<1000; j++) p[j]=j; //роспись выделенной памяти
printf("\np[500]=%d",p[500]); //выборочная проверка
free(p); //освобождение памяти
p=(int *)calloc(1000,sizeof(int)); //запрос чистой памяти
for(s=0,j=0; j<1000; j++) s += p[j];
printf("\ns=%d",s); //алиби - память чистая
free(p); //освобождение памяти
s1=(char *)calloc(20,1); //запрос памяти под строку
strcpy(s1,str); //копирование данных
printf("\ns1=%s",s1); //вывод старой строки
s1=(char*)realloc(s1,8); //перераспределение памяти
s1[5]=0x0; //признак конца новой строки
printf("\ns1=%s",s1); //вывод новой строки
getch();
}
//=== Результат работы ===
s=-2138551277
p[500]=500
s=0
s1=abcdefghijk
s1=abcde
В языке C++ появились дополнительные средства для запроса и освобождения памяти:
q = new type_q; //запрос памяти под скалярную переменную
q = new type_q[n_el]; //запрос памяти под массив из n_el элементов
delete q; //освобождение памяти из-под скаляра
delete []q; //освобождение памяти из-под массива
Динамическое выделение памяти под скалярную переменную можно совместить с ее инициализацией:
int v=new int(5); //после выделения памяти v=5
Память, выделяемую с помощью оператора new под динамические массивы, таким образом инициализировать нельзя.