- •Введение
- •Структура программы на языке Си
- •Директивы препроцессора
- •Константы
- •Переменные
- •Арифметические выражения
- •Операция присваивания
- •Ввод информации с клавиатуры и вывод на экран
- •Логические выражения
- •Операторы управления
- •Оператор условного перехода if
- •Оператор выбора варианта
- •Перечисляемый тип данных
- •Циклы
- •Оператор цикла while
- •Оператор цикла do-while
- •Оператор цикла for
- •Локальные и глобальные переменные
- •Переменные с индексами (массивы)
- •Примеры обработки одномерных массивов
- •Многомерные массивы
- •Массивы и указатели
- •Арифметические и логические операции с указателями
- •Обработка массивов с помощью указателей
- •Обработка массивов переменной размерности
- •Обработка матриц переменной размерности
- •Обработка текстовой информации
- •Стандартные строки языка С++
- •Пример 1. Определение длины строки.
- •Пример 2. Копирование одной строки в другую
- •Пример 3. Исключение из строки символа.
- •Пример 4. Вставка в строку символа
- •Пример 5. Проверка правильности расстановки скобок.
- •Строки типа string
- •Массивы указателей
- •Указатели на функции
- •Сводная таблица форм объявления указателей
- •Преобразование типов
- •Ссылки
- •Структуры
- •Объединения
- •Файлы
- •Чтение файла в матрицу
- •Чтение файла в структуру
- •Функции для обработки произвольных полей структур
- •Связные списки
- •Контейнерные классы
- •Стеки
- •Рекурсия
- •Вопросы для самопроверки
- •Литература
88
индексом), а рабочий указатель матрицы pt сначала настраивается на начало строки (оба указателя совпадают), а затем пробегает по строке на ее длину.
Обработка числовых массивов с помощью указателей не оправдана. Незначительное ускорение вычислений адресов в памяти элементов массива не окупается потерей наглядности программы и сложностью построения границ циклов, поэтому указатели обычно применяются для динамического выделения памяти, для обработки текстовой информации и для возврата сложных объектов (типа структур или классов) из функций.
Обработка массивов переменной размерности
Массивы переменной размерности обычно обрабатывают в подпрограммах, поскольку потребность в таких массивах чаще всего возникает при разработке универсальных функций.
При передаче массивов в функции желательно использовать память вызывающей программы, поэтому размерность массива внутри функции переменная, она целиком определена вызывающей программой. Если возникает необходимость обрабатывать массивы переменной размерности, то память для таких массивов должна резервироваться в вызывающей программе. Безусловно, всегда можно зарезервировать память с "запасом" и использовать только часть ее (т.е. объявить массив максимально возможной размерности). Но такой способ резервирования не позволяет эффективно использовать память.
Более рационально память выделять по мере необходимости и в таком объеме, который требуется для решения задачи. В языке С++ рекомендуется всегда использовать операцию new.
Для вычисления длины элементов данных в байтах рекомендуется использовать операцию sizeof:
sizeof (<имя объекта>) sizeof (<тип>)
89
Эта операция (круглые скобки не обязательны) позволяет определить длину любых элементов данных, в том числе ее можно применять и для определения длины таких типов данных, как структуры и классы. Если в качестве имени объекта записать указатель, то результат чаще всего будет равен 4. Например:
double *w;
n = sizeof (*w);
В результате выполнения этого фрагмента программы n получит значение 8.
Ниже приведен пример программы, вычисляющей сумму элементов массива, размерность которого вводит пользователь в процессе выполнения программы.
// Подсчет суммы элементов массива переменной размерности.
#include "stdafx.h" #include <conio.h> #include <iostream> using namespace std; void _tmain()
{
double *pt,*qt, s; int n;
cout << "Введите размерность массива \n"; cin >> n;
pt = new double[n]; // Выделение памяти для массива s = 0;
cout << "Введите " << n << " элементов массива \n"; for(qt = pt; qt < pt + n; qt++) {
cin >> *qt; s += *qt;
}
cout << "Сумма = " << s; _getch();
}
90
В программе массив явно не объявляется, но для обработки его элементов можно использовать индексы. Читателю предлагается попробовать изменить программу для этого случая.
Обработка матриц переменной размерности
Обработка матриц по строкам и столбцам не адекватна. В частности, для обработки матриц по строкам можно написать функцию для обработки одномерных массивов и вызывать ее в цикле, передавая в нее в качестве аргумента указатель на первый элемент каждой строки. Но по столбцам таким способом выполнить обработку нельзя, поскольку адрес j столбца указывает не на столбец, а на часть строки, начиная с j-го элемента и далее по строке.
Когда матрица является аргументом функции (т. е. имя матрицы записано в списке формальных параметров), то при ее объявлении первую размерность не указывают, поскольку выделенная под нее память определяется в вызывающей программе. Вторая же размерность нужна только для определения длины строк и может быть задана с помощью константы. Так, матрицу, объявленную как int n[3][4], можно рассматривать как одномерный массив, состоящий из 12 элементов, как матрицу 2 х 6 или матрицу 3 х 4 (как объявлено).
Поскольку длина строки матрицы может быть объявляется с помощью константы, то возникают затруднения с обработкой матриц, имеющих переменную длину строк. С целью единообразной обработки матриц, как по строкам, так и столбцам свяжем формулой элементы матрицы и соответствующего ей одномерного массива, элементы которого совпадают с последовательно размещенными в памяти машины элементами матрицы.
Пусть имеется матрица а[2][3]
a[0][0] a[0][1] a[0][1] a[1][0] a[1][1] a[1][1]
Эта матрица размещается в памяти компьютера следующим образом:
91
a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]
ей соответствует одномерный массив в виде
b[0] |
b[1] |
b[2] |
b[3] |
b[4] |
b[5] |
т.е. элементу матрицы a[0][0] соответствует элемент одномерного массива b[0], элементу матрицы a[0][1] соответствует элемент b[1] и т.д. Нетрудно заметить, что если матрица в строке имеет m элементов, то индексы массива b[k] связаны с индексами i и j матрицы a следующим соотношением:
k = m * i + j,
например, при m = 3 и i = 1, j = 1 индекс в массиве b(к) равен 4, т.е. элементу матрицы a[1][1] соответствует элемент b[4].
Если в этой формуле (m для объявленной матрицы равно 3) сначала изменять индекс j (от 0 до 2), а индекс i изменять (от 0 до 1) после достижения индексом j предельного для данной матрицы значения (2), то индекс k одномерного массива b (в соответствии с формулой) будет изменяться в порядке 0, 1, 2, 3, 4, 5, т.е. соответствующая этому массиву матрица будет обрабатываться в порядке
b[0] |
b[1] |
b[2] |
b[3] |
b[4] |
b[5] |
a[0][0] |
a[0][1] |
a[0][2] |
a[1][0] |
a[1][1] |
a[1][2] |
a[0][0] |
a[0][1] |
a[0][1] |
или |
|
|
a[1][0] |
a[1][1] |
a[1][1] |
|
|
|
т.е. по строкам.
Если же сначала изменять индекс i (от 0 до 1), а индекс j изменять (от 0 до 2), то после достижения индексом i предельного для данной матрицы значения (1), индекс k одномерного массива b (в соответствии с формулой) будет изменяться в порядке 0, 3; 1, 4; 2, 5. Таким образом соответствующие этому массиву элементы матрицы будут обрабатываться в порядке
a[0][0] a[1][0] a[0][1] a[1][1] a[0][2] a[1][2]
или
92 a[0][0] a[1][0] //Нулевая строка a[0][1] a[1][1] //Первая строка a[0][2] a[1][2] //Вторая строка
т.е. обработка выполняется по столбцу.
Пересчет индексов матрицы в индексы одномерного массива позволяет обрабатывать матрицы с переменной размерностью, поскольку длина строки матрицы в этом случае может быть параметром (который может и не быть константой) в формуле пересчета индексов. Память же для одномерного массива, в котором количество элементов совпадает с количеством элементов в требуемой матрице, может быть выделена с помощью операции new. Ниже приведен пример обработки матрицы переменной размерности с использованием указанного приема. В программе выполняется суммирование столбцов матрицы.
// Подсчет сумм по столбцам матрицы переменной размерности
#include "stdafx.h" #include <conio.h> #include <iostream> using namespace std;
void msum(double *, double *, int, int); void _tmain()
{
double *a,*b;
int k, m, i; // k – количество строк, m – количество столбцов cout <<"Введите кол-во строк и столбцов матрицы \n"; cin >> k >> m;
// Выделение памяти |
|
a = new double [m * k]; |
// Память для матрицы |
b = new double [k]; |
// Память для вектора |
if(a == NULL || b == NULL) |
|
{ |
|
cout << “Память не выделена”; getch();