Алгоритмизации
Скачать 1.15 Mb.
|
ppp1[0][0][0] . p1[0] ; |
или
puts(“ Введите строку ”); gets(Str);
Остальные операции над строками, как уже отмечалось ранее,
выполняются с использованием стандартных библиотечных функций, декларация прототипов которых находятся в файле string.h.
Приведем наиболее часто используемые стандартные строковые функции.
целое: intatoi(S);
длинное целое: longatol(S);
действительное: doubleatof(S);
целое: itoa(V, S, kod);
длинное целое: ltoa(V, S, kod);
Указателинауказатели
**pp1
m[0] m |
m[1] m |
m[2] |
] m[0][1] m | [0][2] m[0] | [3] | |
] m[1][1] m | [1][2] m[1] | [3] | |
m[2][0] m[ | 2][1] m[2][ | 2] m[2][3] | |
[1][0
(А) (В)
Рис. 10.1. Схема размещения элементов массива mразмером 3×4
Причем в данном случае указательm[1] будет иметь адресm[0]+4*sizeof(int), т.е. каждый первый элемент следующей строки располагается за последним элементом предыдущей строки.
Приведем пример программы конструирования массива массивов: #include
void main()
{
int x0[4] = { 1, 2, 3,4}; // Декларация и инициализация
int x1[4] = {11,12,13,14}; // одномерных массивов
int x2[4] = {21,22,23,24};
int *m[3] = {x0, x1, x2,}; // Создание массива указателей
int i,j;
for (i=0; i<3; i++) {
printf("\n Cтрока %d) ", i+1); for (j=0; j<4; j++)
printf("%3d", m[ i ] [ j ]);
}
}
Результаты работы программы:
Cтрока1)1234
Cтрока2)11121314
Cтрока3)21222324
Такие же результаты будут получены и в следующей программе: #include
void main()
{
int i, j;
int m[3][4] = { { 1, 2, 3, 4}, {11,12,13,14}, {21,22,23,24} };
for (i=0; i<3; i++) {
printf("\n %2d)", i+1); for (j=0; j<4; j++)
printf(" %3d",m[ i ] [ j ]);
}
}
В последней программе массив указателей на соответствующие
массивы элементов создается компилятором автоматически, т.е. данные массива располагаются в памяти последовательно по строкам, что является основанием для декларации массива m в виде
int m[3][4] = {1, 2, 3, 4, 11, 12, 13, 14, 21, 22, 23, 24};
Замена скобочного выражения m[3][4] на m[12] здесь не допускается, так как массив указателей не будет создан.
Таким образом, использование многомерных массивов в языке Си связано с расходами памяти на создание массивов указателей.
Очевидна и схема размещения такого массива в памяти– последовательное (друг за другом) размещение «строк» – одномерных массивов со значениями (векторная организация памяти).
Обращению к элементам массива при помощи операции индексации m[i][j] соответствует эквивалентное выражение, использующее адресную арифметику – *(*(m+i)+j).
Аналогичным образом можно установить соответствие между указателями и массивами с произвольным числом измерений.
Адреснаяфункция
Векторная память поддерживается почти всеми языками высокого уровня и предназначена для хранения массивов различной размерности и различных размеров. Каждому массиву выделяется непрерывный участок памяти указанного размера. При этом элементы, например, двухмерного массива X размерностью n1×n2 размещаются в ОП в следующей последовательности:
Х(0,0), Х(0,1), Х(0,2),... Х(0, n2–1), ..., Х(1,0), Х(1,1), Х(1,2),... Х(1, n2–1), ...,
Х(n1–1,0), Х(n1–1,1), Х(n1–1,2), ..., Х(n1–1, n2–1).
Адресация элементов массива определяется некоторой адресной функцией, связывающей адрес и индексы элемента.
Пример адресной функции для массиваХ:
K(i, j) = n2*i+ j;
где i= 0,1,2,... ,(n1–1); j= 0,1,2,... ,(n2–1); j– изменяется в первую очередь. Адресная функция двухмерного массива A(n,m) будет выглядеть так:
N1 = K(i, j) = m*i+ j, i=0,1,..., n–1; j=0,1,... , m–1 .
Тогда справедливо следующее:
A(i, j) ↔ B(K(i, j)) = B(N1),
B– одномерный массив с размером N1 = n*m.
Например, для двухмерного массива A(2,3) имеем:
(0,0) | (0,1) | (0,2) | (1,0) | (1,1) | (1,2) | – индексы массива А; |
0 | 1 | 2 | 3 | 4 | 5 | – индексы массива В. |
Проведем расчеты:
i= 0, j= 0 N1 = 3*0+0 = 0 B(0) i= 0, j= 1 N1 = 3*0+1 = 1 B(1) i= 0, j= 2 N1 = 3*0+2 = 2 B(2) i= 1, j= 0 N1 = 3*1+0 = 3 B(3) i= 1, j= 1 N1 = 3*1+1 = 4 B(4) i= 1, j= 2 N1 = 3*1+2 = 5 B(5)
Аналогично получаем адресную функцию для трехмерного массива
Х(n1, n2, n3):
K(i, j, k) = n3*n2*i+ n2*j+ k,
где i= 0,1,2,... ,(n1–1); j= 0,1,2,... ,(n2–1); ); k= 0,1,2,... ,(n3–1); значение k–
изменяется в первую очередь.
Для размещения такого массива потребуется участок ОП размером (n1*n2*n3)*sizeof(type). Рассматривая такую область как одномерный массив Y(0,1,..., n1*n2*n3), можно установить соответствие между элементом трехмерного массива Xи элементом одномерного массива Y:
X(i, j, k) ↔ Y(K(i, j, k)) .
Необходимость введения адресных функций возникает лишь в случаях, когда требуется изменить способ отображения с учетом особенностей конкретной задачи.
Работасдинамическойпамятью
Указатели чаще всего используют при работе с динамической памятью, которую иногда называют «куча» (перевод английского слова heap). Это свободная память, в которой можно во время выполнения программы выделять место в соответствии с потребностями. Доступ к выделенным участкам динамической памяти производится только через указатели. Время жизни динамических объектов – от точки создания до конца программы или до явного освобождения памяти.
С другой стороны, некоторые задачи исключают использование структур данных фиксированного размера и требуют введения структур динамических, способных увеличивать или уменьшать свой размер уже в процессе работы программы. Основу таких структур составляют динамические переменные.
Динамическая переменная хранится в некоторой области ОП, не обозначенной именем, и обращение к ней производится через переменную- указатель.
Но вначале рассмотрим еще одну операцию языка Си, основное назначение которой – работа с участками оперативной памяти.
Библиотечныефункции
Функции для манипулирования динамической памятью в стандарте Си следующие:
void*calloc(unsigned n, unsigned size); – выделение памяти для размещения nобъектов размером sizeбайт и заполнение полученной области нулями; возвращает указатель на выделенную область памяти;
void *malloc (unsignedn) – выделение области памяти для размещения блока размером n байт; возвращает указатель на выделенную область памяти;
void *realloc(void *b, unsignedn) – изменение размера размещенного по адресу b блока на новое значение n и копирование (при необходимости) содержимого блока; возвращает указатель на перераспределенную область памяти; при возникновении ошибки, например, нехватке памяти, эти функции возвращают значение NULL, что означает отсутствие адреса (нулевой адрес);
coreleft(void) – получение размера свободной памяти в байтах только для MS DOS(используется в BorlandC++), тип результата: unsigned– для моделей памяти tiny, small и medium; unsignedlong– для моделей памяти compact, large и huge;
voidfree(void *b) – освобождение блока памяти, адресуемого указателем b.
Для использования этих функций требуется подключить к программе в зависимости от среды программирования заголовочный файл alloc.h или malloc.h.
В языке С++ введены операции захвата и освобождения памяти newи
delete, рассматриваемые в разд. 16.4.
Примерсозданияодномерногодинамическогомассива
В языке Си размерность массива при объявлении должна задаваться константным выражением.
Если до выполнения программы неизвестно, сколько понадобится элементов массива, нужно использовать динамические массивы, т.е. при необходимости работы с массивами переменной размерности вместо массива достаточно объявить указатель требуемого типа и присвоить ему адрес свободной области памяти (захватить память).
Память под такие массивы выделяется с помощью функций mallоси calloc(операцией new) во время выполнения программы. Адрес начала массива хранится в переменной-указателе. Например:
int n = 10;
double *b = (double *) malloc(n * sizeof (double));
В примере значение переменной nзадано, но может быть получено и программным путем.
Обнуления памяти при ее выделении не происходит.
Инициализировать динамический массив при декларации нельзя.
Обращение к элементу динамического массива осуществляется так же, как и к элементу обычного – например а[3]. Можно обратиться к элементу массива и через косвенную адресацию – *(а+ 3). В любом случае происходят те же действия, которые выполняются при обращении к элементу массива, декларированного обычным образом.
После работы захваченную под динамический массив память необходимо освободить, для нашего примера free(b);
Таким образом, время жизни динамического массива, как и любой динамической переменной – с момента выделения памяти до момента ее освобождения. Область действия элементов массива зависит от места декларации указателя, через который производится работа с его элементами. Область действия и время жизни указателей подчиняются общим правилам для остальных объектов программы.
Пример работы с динамическим массивом:
#include
{
double *x; int n;
printf("\nВведите размер массива – ");
scanf("%d", &n);
if ((x = (double*)calloc(n, sizeof(*x)))==NULL) { // Захват памяти
puts("Ошибка "); return;
}
...
// Работа с элементами массива
...
free(x); // Освобождение памяти
}
Примеры создания одномерного и двухмерного динамических массивов с использованием операций newи deleteможно посмотреть в разд. 16.4.
Примерсозданиядвухмерногодинамическогомассива
Напомним, что ID двухмерного массива – указатель на указатель. На рис. 10.1 приведена схема расположения элементов, причем в данном случае сначала выделяется память на указатели, расположенные последовательно друг за другом, а затем каждому из них выделяется соответствующий участок памяти под элементы.
. . .
int **m, n1, n2, i, j;
puts(" Введите размеры массива (строк, столбцов): "); scanf(“%d%d”, &n1, &n2);
// Захват памяти для указателей – А(n1=3) m = (int**)calloc(n1, sizeof(int*));
for (i=0; i
*(m+i) = (int*)calloc(n2, sizeof(int)); for ( i=0; i
for ( j=0; j
m[i] [j] = i+j; // *(*(m+i)+j) = i+j;
. . .
for(i=0; i
free(m);
. . .