Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Практикум по программированию на языке Си

..pdf
Скачиваний:
24
Добавлен:
12.11.2023
Размер:
3.53 Mб
Скачать

#define PRINTP(EXPRESSION) \ printf(#EXPRESSION"=%p\n",EXPRESSION)

#include <stdlib.h> int main ()

{

void * pVoid; double * pDouble; int * pointer;

pVoid=malloc(sizeof(long double)); if (pVoid == NULL)

{printf("\n Error!"); return 1;

}

PRINTP(pVoid);

pDouble = (double *)pVoid; PRINTP(pDouble);

*pDouble = 3.1415; PRINTD(*pDouble); pointer = (int *)pVoid; PRINTP(pointer); *pointer = 8765; PRINTI(*pointer); free(pVoid);

return 0;

}

Результаты выполнения программы:

pVoid=8f3e8

pDouble=8f3e8

*pDouble=3.141500e+00

pointer=8f3e8

*pointer=8765

В отличие от предыдущей программы результат, возвращаемый функцией malloc(), непосредственно (без приведения типов) присваивается указателю pVoid. Указатели pVoid, pDouble, pointer имеют одно значение – адрес начала выделенного участка памяти.

ЗАДАНИЕ. Не используйте в предыдущей программе нетипизированный указатель. Результат, возвращаемый функцией malloc(), присвойте указателю типа double *, а затем выполните приведение типа и настройте на тот же участок памяти указатель типа int *.

251

Вариант решения в 07_17_1.с. За исключением значения адреса результаты те же, что и в программе 07_17.с.

Динамически выделяемая память очень удобна для представления массивов, размеры которых не фиксированы на этапе компиляции. При этом не требуется вложения блоков.

ЗАДАЧА 07-18. Введите размер массива, затем его целочисленные элементы. Напечатайте элементы массива в таком порядке: вначале четные, затем кратные трем (но не четные), затем остальные элементы. Печатайте в три колонки.

Задачу решает программа.

/* 07_18.c - сортировка динамического массива */ #include <stdio.h>

#include <stdlib.h> int main ()

{

int size, i, k; int * pArray;

printf("size="); scanf("%d",&size); pArray=(int *)calloc(size,sizeof(int)); if (pArray == NULL)

{printf("\n Error!"); return 0;

}

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

{printf("pArray[%d]=", i); scanf("%d",&pArray[i]);

}

printf("\nResult:");

for (i=0, k=0; i<size; i++) if (pArray[i] % 2 == 0) printf

("%cpArray[%d]=%d",k++%3?'\t':'\n',i,pArray[i]); for (i=0, k=0; i<size; i++)

252

if ((pArray[i] % 2 != 0) && (pArray[i] % 3 == 0)) printf ("%cpArray[%d]=%d",k++%3?'\t':'\n',i,pArray[i]);

for (i=0, k=0; i<size; i++)

if ((pArray[i] % 2 != 0) && (pArray[i] % 3 != 0)) printf ("%cpArray[%d]=%d",k++%3?'\t':'\n',i,pArray[i]);

return 0;

}

Результат выполнения программы:

size=8<ENTER>

pArray[0]=9<ENTER>

pArray[1]=16<ENTER>

pArray[2]=22<ENTER>

pArray[3]=6<ENTER>

pArray[4]=7<ENTER>

pArray[5]=11<ENTER>

pArray[6]=8<ENTER>

pArray[7]=21<ENTER>

Result:

pArray[2]=22

pArray[3]=6

pArray[1]=16

pArray[6]=8

pArray[7]=21

 

pArray[0]=9

 

pArray[4]=7

pArray[5]=11

 

В программе переменная size – размер массива, для которого функцией calloc() выделяется непрерывный участок памяти из size элементов, каждый размером sizeof(int) байтов. Адрес начала участка памяти присваивается указателю pArray. Индексация этого указателя обеспечивает доступ к элементам массива. Выборка из массива и печать выполняются в трех циклах, в каждом из которых проверяется соответствующее условие. Для размещения по столбцам используется вспомогательная переменная int k, значение которой обнуляется

вкаждом заголовке циклов и увеличивается при каждом выполнении

втеле цикла функции printf().

253

Существует принципиальное различие между именем ("настоящего") массива и указателем, адресующим участок памяти, динамически выделенный для элементов массива. Для "настоящего" массива количество элементов определяет соотношение:

sizeof(имя_массива)/sizeof(первый_элемент_массива)

ЭКСПЕРИМЕНТ. Попытайтесь в программе 07-18.с после выполнения функции calloc() вычислить с помощью приведенного выражения количество элементов массива, адресованного указа-

телем pArray.

(Не получится! Значение sizeof(pArray) равно размеру памяти, выделенной только для указателя pArray.)

ЗАДАЧА 07-19. Введите и напечатайте в обратном порядке последовательность целых чисел, количество которых заранее не фиксировано. Считайте концом последовательности ввод нулевого значения.

Будем помещать вводимые числа в динамический массив, длина которого увеличивается по мере чтения новых ненулевых значений. Начало массива (участка памяти для него) адресуем указателем int *pArray. Для ввода чисел используем "бесконечный" цикл с заголовком while(1). Выход из цикла – оператор break, исполняемый после ввода нулевого значения. Счетчик int counter пусть сохраняет количество введенных чисел и используется при обращении к функции realloc(). Программа, решающая задачу:

/*07_19.c – rellloc и растущий динамический массив*/ #include <stdio.h>

#include <stdlib.h> int main()

{

int i, number, counter=0; int * pArray = NULL;

printf("\nInput integer values:\n"); while(1)

{printf("number="); scanf("%d",&number); if (number == 0) break;

counter++;

pArray=

254

(int *)realloc(pArray,counter*sizeof(int)); pArray[counter-1]=number;

}

printf("Result: "); for(i=counter-1; i>=0; i--)

printf("%d ",pArray[i]); free (pArray);

}

Результаты выполнения программы:

Input integer values: number=2<ENTER> number=4<ENTER> number=5<ENTER> number=2<ENTER> number=8<ENTER> number=4<ENTER> number=0<ENTER>

Result: 4 8 2 5 4 2

ЗАДАНИЕ. Исключите из предыдущей программы функцию realloc(), точнее, замените ее использование функциями calloc() и free().

Вариант решения представляет следующая программа:

/*07_19_1.c - растущий динамический массив без relloc()*/

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

{

int i, number, counter=0; int * oldArray, * newArray;

printf("\nInput integer values:\n"); while(1)

{printf("number=");

scanf("%d",&number);

if (number == 0) break; counter++;

newArray=(int *)calloc(counter,sizeof(int));

255

for(i=0;i<counter-1;i++) newArray[i]=oldArray[i];

newArray[counter-1]=number; free (oldArray); oldArray=newArray;

}

printf("Result: "); for(i=counter-1;i>=0;i--)

printf("%d ",newArray[i]); free (oldArray);

}

Результаты выполнения программы:

Input integer values: number=2<ENTER> number=4<ENTER> number=5<ENTER> number=2<ENTER> number=8<ENTER> number=4<ENTER> number=0<ENTER>

Result: 4 8 2 5 4 2

ЗАДАНИЕ. Используйте для решения задачи (07_19.с) вместо realloc() функции malloc() и free().

В приведенных решениях задачи 07-19 есть один недостаток – память с помощью функций realloc(), calloc() выделяется небольшими порциями. Но выполнение функций динамического выделения памяти занимает относительно много времени. При обработках больших объемов данных лучше увеличивать память большими "блоками", но делать это редко. При этом потребуется "наблюдать" за "расходом" уже выделенной памяти, что немного усложнит алгоритм.

ЗАДАНИЕ. Напишите программу решения задачи 07-19, в которой память выделяется блоками по мере необходимости. Размер блока задайте препроцессорной константой.

256

7.6. Моделирование многомерных динамических массивов

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

ЗАДАЧА 07-20. Cформируйте и напечатайте единичную матрицу с вещественными элементами, вводя ее порядок (n).

Решение:

/* 07_20.c - матрица с переменными размерами */ #include <stdio.h>

#include <stdlib.h> int main()

{

int i,j, n; double **matr;

printf("Input matrix order:"); scanf("%d",&n);

matr=(double **)calloc(n,sizeof(double *)); for(i=0; i<n; i++)

{ matr[i]=(double *)calloc(n,sizeof(double)); for (j=0; j<n; j++)

matr[i][j] = (i==j ? 1.0 : 0.0);

}

printf("Result: "); for(i=0; i<n; i++)

{printf("\n");

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

printf("\t%5.2f",matr[i][j]); free (matr[i]);

}

free (matr);

}

257

Результаты выполнения программы:

Input matrix order:4<ENTER>

Result:

1.00

0.00

0.00

0.00

0.00

1.00

0.00

0.00

0.00

0.00

1.00

0.00

0.00

0.00

0.00

1.00

В программе матрица представлена динамическим массивом указателей с n элементами типа double * и набором из n динамических массивов, каждый из n элементов типа double. Каждому из массивов память выделена функцией calloc(). Адрес массива указателей присваивается указателю double ** matr. Индексация этого указателя, т.е. выражение matr[i], позволяет получать доступ к i-му элементу динамического массива указателей. Именно так в цикле массив указателей "настраивается" на адреса (на начала) массивов с элементами типа double. Двойная индексация matr[i][j] обеспечивает доступ к вещественным элементам, в совокупности формирующим матрицу. Остальное очевидно. Отметим только порядок освобождения памяти. Вначале в цикле "разрушаются" массивы, адресуемые элементами массива указателей, затем уничтожается массив указателей обращением free(matr).

ЗАДАНИЕ. Замените везде в программе индексацию явным разыменованием, т.е. обойдитесь без квадратных скобок.

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

ЗАДАЧА 07-21. Определите набор динамических массивов для представления нижней треугольной матрицы. Размеры матрицы вводятся при выполнении программы, а ее ненулевые элементы получают значения номеров строк, в которых они размещены.

Напомним, что матрица [aik] называется нижней треугольной, если из i<k следует aik==0. Здесь i – номер строки, k – номер столбца (1in, 1km, n – число строк, m – число столбцов, рис. 7.3).

258

Рис. 7.3. Нижняя треугольная матрица (n=4, m=6)

Особенность и цель задачи – возможность представления в памяти не всех элементов матрицы, а только ненулевых. Для элементов строк треугольной матрицы используем массивы, длины которых равны номерам строк.

Пусть double ** triMatr – указатель на массив указателей с элементами типа double *. Переменная nRow – число строк нижней треугольной матрицы, переменная nColumn – число ее столбцов. Массивы для представления ненулевых элементов матрицы имеют разную длину (рис. 7.4). Для j-й строки нужен массив из j элементов. Еще раз обратите внимание на то, что элементы матриц нумеруются начиная с [1,1], а индексация массивов в языке Си начинается с 0.

Рис. 7.4. Массивы для представления нижней треугольной матрицы при n=4

259

/* 07_21.c - непрямоугольный двумерный "массив" */ #define READI(VARIABLE) \

{printf(#VARIABLE"="); scanf("%d",&VARIABLE);} #include <stdio.h>

#include <stdlib.h> int main()

{

int i,j, nRow, nColumn, jRow; double **triMatr; READI(nRow);

READI(nColumn);

triMatr=(double **)calloc(nRow,sizeof(double *)); for(i=0; i<nRow; i++)

{ jRow = (i < nColumn ? i+1 : nColumn); triMatr[i]=(double *)calloc(jRow,sizeof(double));

for (j=0; j<jRow; j++) triMatr[i][j]=i+1;

}

printf("Result: "); for(i=0; i<nRow; i++)

{printf("\n");

jRow = (i < nColumn ? i+1 : nColumn); for (j=0; j<jRow; j++)

printf("\t%5.2f",triMatr[i][j]); free (triMatr[i]);

}

free (triMatr);

}

Результаты первого выполнения программы:

nRow=3<ENTER>

nColumn=5<ENTER>

Result:

1.00

2.002.00

3.003.00 3.00

Результаты второго выполнения программы:

nRow=6<ENTER>

nColumn=4<ENTER>

Result:

260