Лекция 6.

Массивы. Их связь с указателями. Динамические массивы.

Пример 1. Рассмотрим задачу сортировки 3 чисел по возрастанию. Считаем числа с клавиатуры в переменные a, b и c, поменяем значения a, b, c между собой так, чтобы выполнялись неравенства a < b < c, а затем выведем числа a, b, c на экран.


#include <stdio.h>

int main(void) {

int a, b, c, buf;

scanf(“%d%d%d”, &a, &b, &c);

/* Поместим в a самое маленькое число */

if ((b < a) && (b < c)) {

buf = a; a = b; b = buf;

} else if ((c < a) && (c < a)) {

buf = a; a = c; c = buf;

}

/* Поместим в b самое маленькое из чисел b и c */

if (c < b) {

buf = b; b = c; c = buf;

}

printf(“%d %d %d\n”, a, b, c);

return 0;

}


Программа получилась довольно громоздкая и запутанная.

Теперь представим, что надо отсортировать 1000 чисел. Для того, чтобы прочитать эти числа с клавиатуры или из файла, необходимо завести 1000 переменных. А затем придется перебрать каждую из 999 переменных и сравнить их с оствшимися 999 переменными, чтобы проверить, не больше ли эта переменная всех остальных и не надо ли поместить ее значение в последнюю из переменных. И так 999 раз: для каждого из мест 2-1000.

Чтобы облегчить работу с большим количеством однотипных переменных, в языках высокого уровня имеется тип данных «таблица».

В языке C, как и в других языках высокого уровня, есть механизм создания таблиц (массивов, векторов), содержащих несколько переменных одного типа. Для этого нужно указать тип элементов, имя таблицы и количество элементов в таблице, причем количество элементов в таблице должно быть константой. Например :


int array[1000];


К переменным внутри таблицы можно обращаться, используя имя таблицы и индекс (номер) переменной — целое число, например, array[0] или array[n], где n – целая переменная.

Индексы переменных в таблице из N переменных — это числа в диапазоне от 0 до N-1.


Пример 2. Программа сортировки 1000 чисел методом поиска минимального элемента:

#include <stdio.h>

int main(void) {

int array[1000];

int buf;

int i;

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

scanf(“%d “, &array[i]);

/* Для каждого элемента таблицы с индексом 0, …, 999 */

for (i = 0; i < 1000; i++) {

/* Находим индекс минимального элемента из элементов i, …, 1000 */

imin = i;

for (j = 0; j < 1000; j++) {

if (array[j] < array[imin]) {

imin = j;

}

}

/* Ставим минимальный элемент на i-ое место: меняем значения array[imin] и array[i] */

if (i != imin) {

buf = array[i];

array[i] = array[imin];

array[imin] = buf;

}

}

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

printf(“%d “, array[i]);

printf(“\n”);

return 0;

}


Связь массивов и указателей.

Указатели в языке C обладают следующим свойством: при увеличении значения указателя на 1 адрес, который в нем содержится меняется не на 1 байт, а на то количество байтов, которое содержится в элементах того типа, на которые он указывает.

Таким образом, если указатель содержит адрес какой-то переменной, то при увеличении значения указателя на 1 он будет указывать на байт памяти, следующий за этой переменной.

Элементы таблицы (массива) расположены в оперативной памяти поряд.

Поэтому для «прохода » по массиву можно использовать указатели.

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

Таким образом, присвоив указателю адрес первого элемента массива и последовательно увеличивая его значение на 1, можно «пробежать» этим указателем по всем элементам массива, как это делается в примере 4.

Адрес первого элемента массива можно получить, используя оператор &.

Например, для массива целых чисел array это получение выглядит так:


int *p = &array[0];


Другой способ:


int *p = array;


Второй способ возможен, так как имя массива является его адресом, а именно указателем на его первый элемент, то есть на элемент с индексом 0.


Пример 3. Программа подсчета количества отрицательных элементов в массиве.

#include <stdio.h>

int main(void) {

int array[1000];

int *p;

int i;

int res = 0;

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

scanf(“%d “, &array[i]);

p = array;

for (i = 0; i < 1000; i++) {

if (*p < 0)

++res;

}

printf(“Num of negative numbers: %d “, res);

return 0;

}


Выделение динамической памяти под массивы

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

Например, при заполнении массива числами, находящимися в файле, можно узнать количество этих чисел и захватить количество байтов памяти, достаточное для размещения элементов массива, а затем считать числа из файла в эту память.

Количество чисел можно узнать, прочитав файл.

Количество байтов, используемых для размещения в памяти одного числа, можно узнать при помощи оператора sizeof(), например, оператор sizeof(int) возвращает количество байтов в целом числе.

Функция malloc() возвращает абстрактный адрес (void*) , который перед присвоением адресу массива надо преобразовать в указатель на тип элементов, хранящихся в массиве. Преобразование типа любого выражения осуществляется помещением нового типа в круглых скобках перед этим выражением, например: (int*)malloc(num * sizeof(int));

После окончания работы с массивом захваченную память необходимо освободить при помощи функции free().

Функции malloc() и free() реализованы в стандартной библиотеке, подключаемой автоматически, а задекларированы в файле <stdlib.h>


Пример 4. Программа сортировки чисел, находящихся в файле «tmp.dat”. Отсортированные числы выводятся в файл «tmp.res”.

#include <stdio.h>

#include <stdlib.h>

int main(void) {

FILE *IN, *OUT;

int *array;

int i;

int num = 0;

IN = fopen(“tmp.dat”, “r”);

if (IN == NULL) {

printf(“File not opened\n”);

return -1;

}

while (fscanf(IN, “%d”, &x) == 1)

++num;

fclose(IN);

if (num == 0) {

printf(“File tmp.dat is empty\n”);

return 0;

}

array = (int*)malloc(sizeof(int) * num);

IN = fopen(“tmp.dat”, “r”);

if (IN == NULL) {

printf(“File not opened\n”);

return -1;

}

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

fscanf(IN, “%d “, &array[i]);

fclose(IN);

for (i = 0; i < 1000; i++) {

/* Находим индекс минимального элемента из элементов i, …, 1000 */

imin = i;

for (j = 0; j < 1000; j++) {

if (array[j] < array[imin]) {

imin = j;

}

}

/* Ставим минимальный элемент на i-ое место: меняем значения array[imin] и array[i] */

if (i != imin) {

buf = array[i];

array[i] = array[imin];

array[imin] = buf;

}

}

OUT = fopen(“tmp.res”, “w”);

if (OUT == NULL) {

printf(“File tmp.res not opened\n”);

return -1;

}

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

fprintf(OUT, “%d\n“, array[i]);

fclose(OUT);

free(array);

return 0;

}