
- •Структура программы на языке Си. Этапы выполнения программы
- •1.1. Алфавит языка Си
- •1.2. Лексемы
- •1.3. Идентификаторы и ключевые слова
- •1.4. Знаки операций
- •1.5. Литералы (константы)
- •1.6. Комментарии
- •1.7. Общая структура программы на языке Си.
- •1.8. Функциональная и модульная декомпозиции
- •1.9. Этапы обработки программы.
- •1.10. Роль препроцессора.
- •1.11. Ошибки
- •2. Переменные и константы. Типы данных
- •2.1. Основные типы данных
- •2.2. Декларация (объявление) объектов
- •2.3. Константы в программах
- •2.4. Целочисленные константы
- •2.5. Константы вещественного типа
- •2.6. Символьные константы
- •2.7. Строковые константы
- •3. Обзор операций
- •3.1. Операции, выражения
- •3.2. Арифметические операции
- •3.3. Операции сравнения
- •3.4. Логические операции
- •4. Операции (продолжение).
- •4.1. Операция присваивания
- •Примеры недопустимых выражений:
- •4.2. Сокращенная запись операции присваивания
- •4.3. Преобразование типов операндов бинарных операций
- •4.4. Преобразование типов при присваивании.
- •4.5. Операция явного приведения типа
- •4.6. Операция «,» (запятая)
- •5. Стандартная библиотека языка Си
- •5.1. Стандартные математические функции
- •5.2. Потоковый ввод-вывод
- •5.3. Консольные функции вывода данных на экран
- •5.4. Консольные функции ввода информации
- •5.5. Ввод-вывод в оконных приложениях.
- •Советы по программированию
- •6. Операторы языка с.
- •7. Составление разветвляющихся алгоритмов
- •7.1. Условные операторы
- •If (выражение) оператор;
- •If (выражение) оператор 1 ;
- •If (выражение 1) оператор 1;
- •If (выражение 2) оператор 2;
- •If (выражение 3) оператор 3;
- •7.2. Оператор выбора альтернатив (переключатель)
- •7.3. Условная операция «? :»
- •8. Составление циклических алгоритмов
- •8.1. Оператор с предусловием while
- •8.2. Оператор цикла с постусловием do – while
- •8.3. Оператор цикла с предусловием и коррекцией for
- •8.4. Вложенные циклы.
- •9. Операторы передачи управления.
- •9.1. Оператор безусловного перехода goto
- •9.2. Операторы continue, break и return
- •10. Массивы
- •10.1. Одномерные массивы
- •10.2. Примеры алгоритмов, использующих одномерные массивы.
- •10.3. Многомерные массивы
- •10.4. Примеры алгоритмов, использующих двумерные массивы.
- •10.5. Компонента StringGrid
- •11. Размещение данных и программ в памяти пэвм
- •11.1. Общие понятия.
- •11.2. Кодирование целых чисел.
- •11.3. Кодирование вещественных чисел.
- •11.4. Кодирование символов.
- •Примеры кодов символов:
- •Стандартная часть таблицы символов (ascii)
- •Дополнительная часть таблицы символов
- •11.5. Операция sizeof
- •11.6. Побитовые логические операции. Операции над битами
- •11.7. Кодирование программы.
- •11.8. Регистры
- •12.1. Строки как нуль-терминированные массивы char.
- •12.2. Русификация консольных приложений.
- •12.3. Строки как переменные типа AnsiString.
- •12.4. Преобразования строковых типов.
- •12.5. Тип String в консольных приложениях.
- •13. Функции пользователя и классы памяти.
- •13.1. Сущность и предназначение функций.
- •13.2. Определение и вызов функции.
- •13.3. Прототип функции.
- •13.4. Область видимости.
- •13.5. Классы памяти объектов в языке Cи.
- •13.6. Разбиение программы на модули.
- •14. Структуры и объединения
- •14.1. Понятие структуры
- •14.2. Декларация структурного типа данных
- •14.3. Объявление структурных переменных
- •14.4. Обращение к полям структуры
- •14.5. Операции со структурой как единым целым
- •14.6. Вложенные структуры
- •14.7. Массивы структур
- •14.8. Размещение структурных переменных в памяти
- •14.9. Битовые поля
- •14.10. Объединения
- •15. Генерация псевдослучайных чисел.
- •16. Файлы в языке с
- •16.1. Типы файлов.
- •16.2. Открытие файла
- •16.3. Закрытие файла
- •16.4. Запись - чтение информации
- •А) Посимвольный ввод-вывод
- •Б) Построчный и форматированный ввод-вывод
- •В) Блоковый ввод-вывод
- •Int fflush(file *stream);
- •16.5. Текстовые файлы
- •16.6. Перенаправление стандартного ввода-вывода
- •16.7. Бинарные файлы
- •16.8. Дополнительные полезные функции
- •16.9. Простейший пример создания собственной базы данных
- •17. Указатели
- •17.1. Определение указателей
- •17.2. Связь указателей и массивов.
- •17.3. Операции над указателями (косвенная адресация)
- •17.4. Операции над указателями (косвенная адресация)
- •17.5. Массивы указателей.
- •17.6. Указатели на указатели.
- •17.7 . Указатели как параметры функций.
- •Void f1(int, const double *);
- •17.8 . Указатели на структуры
- •17.9. Ссылка
- •17.10. Указатели на функции
- •Id_функции(список аргументов);
- •18. Работа с динамической памятью
- •18.1. Динамическое выделение и освобождение памяти.
- •18.2. Создание одномерного динамического массива.
- •18.3. Создание двуxмерного динамического массива.
- •19. Операция typedef
- •20. Отладка и пошаговое выполнение программы
18.2. Создание одномерного динамического массива.
В языке С размерность массива при объявлении должна задаваться константным выражением. При необходимости работы с массивами переменной размерности нужно объявить вместо массива указатель требуемого типа, а затем в момент необходимости выделить память под массив нужного размера. Таким образом, размер массива в любом случае должен быть указан до начала работы с ним; но при динамическом выделении памяти он указывается не в тексте программы, а непосредственно при захвате памяти, и поэтому может определяться значением переменной.
Формат операции new для массивов:
указатель = new тип [количество] ;
Результат операции new, присваиваемый указателю – адрес начала области памяти для размещения данных указанного количества и типа. При нехватке памяти – результат NULL.
Формат операции delete:
delete указатель;
либо: delete [] указатель; (эти записи равносильны)
После выделения памяти с этим массивом можно работать, аналогично работе с обычным массивом, используя вышеописанную операцию косвенной адресации [].
Пример:
...
double *x;
int i, n;
...
puts(" Введите размер массива: ");
scanf(“%d”, &n);
x = new double [n] ;
if (x == NULL) {
puts(" Предел размерности ! ");
return;
}
for (i=0; i<n; i++) // Ввод элементов массива
scanf(“%lf”, &x[i]); // Косвенная адресация
...
delete [ ]x; // Освобождение памяти
...
18.3. Создание двуxмерного динамического массива.
Операция new способна выделить память лишь под одномерный массив. А как быть, если массив двумерный?
Наиболее удобный способ - это представить двумерный массив как массив из массивов, т.е каждую строку матрицы - как одномерный массив. При этом под каждую строку матрицы память будет выделена по отдельности, как под одномерный массив; и на нее потребуется отдельный указатель. Поскольку строк в матрице может быть много, указателей тоже будет много, и уже для их хранения потребуется вспомогательный массив из указателей. А так как количество строк в матрице переменного размера заранее неизвестно, то этот вспомогательный массив тоже будет динамическим, и сначала нужно выделить память под него. Декларировать при этом придется только указатель на вспомогательный массив; он будет, таким образом, указателем на указатель (**).
Пример. Создать динамическую матрицу размера n1 х n2. Присвоить каждому ее элементу значение, равное сумме номеров его строки и столбца:
...
int **m, n1, n2;
puts("Vvedite chislo strok i stolbtsov");
scanf(“%d%d”, &n1, &n2);
m = new int * [n1]; // Захват памяти для массива указателей -
// на рисунке А (n1=3)
for ( int i=0; i<n1; i++) // Захват памяти для каждого из
m[i] = new int [n2]; // массивов элементов (n1 таких массивов)
// - на рисунке B (n2=4)
. . .
for ( i=0; i<n1; i++)
for ( j=0; j<n2; j++)
m[i][j] = i+j; // *(*(m+i)+j) = i+j;
. . .
for ( i=0; i<n1; i++) // Освобождение памяти
delete m[i];
delete m;
. . .
Устройство полученной "динамической матрицы" изображено на Рис. 19.1. Здесь имеется вспомогательный массив из 3 указателей и 3 массива для хранения данных, в каждом - по 4 элемента. Указатель m указывает на массив указателей, а каждый из указателей - на свою строку матрицы:
-
Указатели
m[0]
m[0][0]
m[0][1]
m[0][2]
m[0][3]
m[i][j] ↔ *(*(m+i)+j)
m[1]
m[1][0]
m[1][1]
m[1][2]
m[1][3]
m[2]
m[2][0]
m[2][1]
m[2][2]
m[2][3]
(А) (В)
Рис. 4
Несмотря на сложность полученной схемы, работа с ней предельно проста. Обращение к элементу матрицы имеет точно такой же вид, как и к элементу "обычного" двумерного массива:
m[i][j]
а выделение и освобождение динамической памяти для матрицы стандартно и не зависит от конкретной задачи:
m = new int * [n1]; // Выделение памяти
for ( int i=0; i<n1; i++)
m[i] = new int [n2];
. . .
for ( i=0; i<n1; i++) // Освобождение памяти
delete m[i];
delete m;
Преимущества динамических матриц:
-
Ее размер ограничен лишь размером доступной в данный момент памяти;
-
Под динамическую матрицу выделяется не больше памяти, чем требуется;
-
Ее строки могут быть не только одинаковой, но и разной длины (если нужно);
-
Чтобы поменять местами ее строки, не обязательно выполнять поэлементный обмен в цикле. Можно вместо этого переприсвоить указатели:
int *w=m[0];
m[0]=m[1];
m[1]=w;
Хотя все элементы матрицы остались на месте, 0-ая и 1-ая строки всё же поменялись местами, т.к. m[0] указывает теперь на бывшую 1-ую строку (и наоборот), и значение m[0][i] будет таким, каким до обмена было m[1][i].