Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Рацеев С.М. Программирование на языке Си.pdf
Скачиваний:
369
Добавлен:
23.03.2016
Размер:
1.65 Mб
Скачать

7.8. Массивы указателей

Массивы можно формировать также и из указателей. Очень часто массивы указателей используются для работы с массивами строк. Каждый элемент такого массива является указателем на некоторую строку, или, более точно, каждый элемент содержит адрес нулевого символа соответствующей строки.

Например, объявим и инициализируем массив указателей color, состоящий из названий трех цветов (составляющие цвета в компьютерной графике):

char *color[3] = {“red”, “green”, “blue”};

Схематично массив указателей color можно изобразить следующим образом:

Каждый указатель ссылается на нулевой символ соответствующей строки. Поэтому массив указателей color может ссылаться на строки произвольной длины.

Рассмотрим еще один пример. Создадим массив указателей из 10 элементов, в котором будут храниться адреса массивов целого типа, причем массивы будут иметь различную длину, например:

#include <stdio.h> #include <stdlib.h> int main( )

99

{

int *a[10] = {NULL}; /* инициализируем все элементы массива нулями */ int i, j;

for (i = 0; i < 10; i++)

{

a[i] = (int *)calloc((i+1), sizeof(int)); for (j = 0; j <= i; j++)

a[i][j] = j*10 + 10;

}

for (i = 0; i < 10; i++)

{

for (j = 0; j <= i; j++) printf("%d ", a[i][j]);

printf("\n");

}

for (i = 0; i < 10; i++)

{

free(a[i]); a[i] = NULL;

}

return 0;

}

7.9. Двумерные массивы переменного размера

Создать динамический двумерный массив можно несколькими способами. Рассмотрим некоторые из них. В качестве примеров будем рассматривать динамические матрицы размера m на n типа double. Данный тип можно заменить на любой другой тип данных.

1 способ. Самый простой способ это выделить в памяти компьютера динамический (одномерный) массив a размера m*n*sizeof(double) байт. То есть всего данный массив будет содержать m*n элементов типа double. Если (условно) разделить данные m*n элементы на блоки длины n, то получим ровно m блоков. Каждый такой блок будет соответствовать очередной строке матрицы. Тогда элемент матрицы с индексами i и j имеет адрес a+i*n+j в массиве a, при этом *(a+i*n+j) является значением данного эле-

100

мента. Итак, обращение к элементам матрицы будет иметь следующий вид:

*(a+i*n+j), 0≤ i < m, 0 ≤ j < n.

Проиллюстрируем это на следующем примере.

#include<stdio.h>

#include<stdlib.h>

/* размещение одномерного массива в памяти компьютера */ double *Allocate(int m, int n)

{

return (double *)malloc(m*n*sizeof(double));

}

/* инициализация элементов массива */ void Init(double *a, int m, int n)

{

int i, j;

for(i = 0; i < m; i++) for(j = 0; j < n; j++)

*(a + i*n + j) = rand()%10;

}

/* вывод элементов массива на экран */ void Print(double *a, int m, int n)

{

int i, j;

for (i = 0; i < m; i++)

{

for (j = 0; j < n; j++) printf("%.1f ", *(a + i*n + j));

printf("\n");

}

}

int main()

{

double *a; /* динамический массив */

101

int m, n;

scanf("%d%d", &m, &n); a = Allocate(m, n);

if (a)

{

Init(a, m, n); Print(a, m, n); free(a);

}

return 0;

}

2 способ. В данном случае также для двумерного массива выделяется один блок памяти, только немного большего размера, чем в предыдущем случае, где обращение к элементам происходило по правилу *(a+i*n+j). Чтобы использовать привычное обращение к элементам (динамической) матрицы a[i][j] необходимо к блоку размером в m*n*sizeof(double) байт добавить дополнительный блок, имеющий размер m*sizeof(double *) байт, в котором будут храниться указатели на соответствующие строки матрицы. Данный блок поместим перед блоком с основными данными (значениями матрицы).

#include<stdio.h>

#include<stdlib.h>

/* размещение массива в памяти компьютера */ double **Allocate(int m, int n)

{

double **a; int i;

a = (double **)malloc(m*n*sizeof(double) + m*sizeof(double *)); if (a)

{

for(i = 0; i < m; i++)

a[i] = (double *)(a + m) + i*n; return a;

}

102

else return NULL;

}

/* инициализация элементов массива */ void Init(double **a, int m, int n)

{

int i, j;

for(i = 0; i < m; i++) for(j = 0; j < n; j++)

a[i][j] = rand()%10;

}

/* вывод элементов массива на экран */ void Print(double **a, int m, int n)

{

int i, j;

for (i = 0; i < m; i++)

{

for (j = 0; j < n; j++) printf("%.1f ", a[i][j]);

printf("\n");

}

}

int main()

{

double **a; int m, n;

scanf("%d%d", &m, &n); a = Allocate(m, n);

if (a)

{

Init(a, m, n); Print(a, m, n); free(a);

}

return 0;

}

103

3 способ. Рассмотрим другой способ, который является наиболее часто применяемым и благоприятным для изменения размеров двумерного массива. Если в двух предыдущих случаях создавался единый монолитный блок памяти, то для создания двумерного массива в данном случае сначала требуется выделить память для одномерного массива указателей на одномерные массивы, а затем выделить память для одномерных массивов.

Например, пусть требуется создать динамический двумерный массив a[m][n] типа double:

В представленной ниже программе функция Dispose() в цикле проходит по массиву указателей и освобождает память, занимаемую каждым элементом, а затем освобождает память, занимаемую самим массивом указателей.

Функция Allocate() сначала выделяет память для массива указателей, а затем в цикле проходит по данному массиву указателей и выделяет память для одномерных массивов. Если на каком-то шаге итерации память выделить невозможно, то вызывается функция Dispose() для освобождения успешно выделенной памяти для некоторых одномерных массивов, а сама функция Allocate() возвращает значение NULL. При успешном выделении памяти для всего двумерного массива функция Allocate() возвращает указатель на одномерный массив указателей.

#include<stdio.h>

#include<stdlib.h>

/* освобождение памяти, занимаемой двумерным массивом a */ void Dispose(double **a, int m)

{

int i;

for (i = 0; i < m; i++)

104

if (a[i] != NULL) free(a[i]);

free(a);

}

/* выделение памяти под двумерный массив a */ double **Allocate(int m, int n)

{

double **a; int i;

int flag; /* логическая переменная */

a = (double **)malloc(m * sizeof(double *)); if (a != NULL)

{

i = 0; flag = 1;

while (i < m && flag)

{

a[i] = (double *)malloc(n * sizeof(double)); if (a[i] == NULL)

flag = 0; else i++;

}

if (!flag)

{

Dispose(a, m); a = NULL;

}

}

return a;

}

/* заполнение элементов двумерного массива */ void InitArray(double **a, int m, int n)

{

int i, j;

for (i = 0; i < m; i++)

105

for (j = 0; j < n; j++) a[i][j] = rand()%10;

}

/* вывод на экран элементов двумерного массива */ void PrintArray(double **a, int m, int n)

{

int i, j;

for (i = 0; i < m; i++)

{

for (j = 0; j < n; j++) printf("%.1f ", a[i][j]);

printf("\n");

}

}

int main( )

{

double **a; int m, n;

scanf("%d%d", &m, &n); a = Allocate(m, n);

if (a != NULL)

{

InitArray(a, m, n); PrintArray(a, m, n); Dispose(a, m);

a = NULL;

}

return 0;

}

106