Дополнительные сведения о подпрограммах и массивах



При объявлении в заголовке функции формальных параметров, являющихся двумерными массивами (или массивами с большим числом измерений), приходилось задавать максимальное количество элементов по каждой размерности, начиная со второй. Этого не надо делать только в том случае, когда работа с таким массивов ведется как с одномерным. Такой подход приводит к нерациональному использованию оперативной памяти и не позволяет создавать полностью универсальные и независимые от внешних параметров функции.

Например, если функция должна обрабатывать матрицу x[mmax][nmax], где mmax≤30, nmax≤20, а реальные значения количеств строк и столбцов будут меньше указанных, то произойдет выделение лишних объемов памяти. Для матрицы a[10][10] потери составят 500 ячеек памяти, а для матрицы b[5][5] – 575 ячеек.

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

 

Динамические массивы

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

Для объявления таких массивов и работы с ними используются указатели. Выделение памяти может производиться с помощью стандартных функций или с помощью операции new.

Прототипы функций динамического распределения памяти находятся в заголовочном файле <stdlib.h>.

Функция calloc()  выделяет память, размер которой равен значению выражения num*size, т.е. память, достаточную для размещения массива, содержащего num элементов размером size. При этом все биты распределенной памяти инициализируются нулями. Прототип функции void *calloc(size_t num, size_t size);.

Функция возвращает указатель на первый байт выделенной памяти. Если отсутствует требуемый объем памяти, то возвращается нулевой указатель. Перед попыткой использования распределенной памяти необходимо проверять возвращаемое значение на ноль.

Следующая программа демонстрирует выделение памяти под одномерный массив, ввод и вывод элементов этого массива.

// одномерный динамический массив – выделение памяти с помощью функции

//  calloc

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

#include "stdlib.h"

int _tmain(int argc, _TCHAR* argv[])

{ float *a;

int n;

printf("wwedite kol-wo elementow mfssiwa\n");

scanf("%d",&n);

a=(float *)calloc(n,sizeof (float));

if (!a) printf("\n oschibka");

else {printf("\nwwedite elementi\n");

for(int i=0;i<n;i++)

scanf("%f",&a[i]);

       printf("\nmassiw\n");

   for(int i=0;i<n;i++)

printf("%5.1f ",a[i]);

}

getch();

 return 0;

 }

 

Функция malloc() возвращает указатель на первый байт области памяти размером size, которая выделяется в динамической области памяти. Если в динамической памяти отсутствует требуемый объем памяти, то возвращается нулевой указатель. Прототип функции void *malloc(size_t size). В отличие от предыдущей функции malloc() не инициализирует выделенную память определенными значениями. Следующая программа демонстрирует работу с функцией malloc().

//  одномерный динамический массив – выделение памяти с помощью функции

//  malloc

 

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

#include "stdlib.h"

int _tmain(int argc, _TCHAR* argv[])

{ float *a;

int n;

printf("wwedite kol-wo elementow massiwa\n");

scanf("%d",&n);

a=(float *)malloc(n*sizeof (float));

if (!a) printf("\n oschibka");

else {printf("\nmassiw\n");

   for(int i=0;i<n;i++)

printf("%5.1f ",a[i]);

printf("\nwwedite elementi\n");

for(int i=0;i<n;i++)

scanf("%f",&a[i]);

       printf("\nmassiw\n");

   for(int i=0;i<n;i++)

printf("%5.1f ",a[i]);

}

getch();

return 0;

}

Для изменения размеров ранее выделенного блока памяти используется функция realloc().   Прототип функции void *realloc(void *ptr, size_t size). Указатель ptr задает адрес ранее выделенной области памяти, а второй параметр size определяет новый объем выделяемой области памяти. Значение этого параметра может быть больше или меньше объема ранее выделенной памяти. Функция возвращает указатель на блок памяти. Поскольку при увеличении выделяемой памяти она может занимать новую область, то в этом случае содержимое старого блока копируется в новую область.

Если указатель ptr имеет нулевое значение, то функция realloc() просто выделяет требуемое количество байт памяти и возвращает указатель на эту область памяти. Функция realloc(). не инициализирует выделенную память определенными значениями. Если значение параметра size равно нулю, то адресуемая первым параметром память освобождается. Следующая программа демонстрирует работу с функцией realloc().

// пример использования функции realloc

 

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

#include "stdlib.h"

int _tmain(int argc, _TCHAR* argv[])

{ float *a;

int n,m;

a=NULL;

printf("wwedite kol-wo elementow massiwa\n");

scanf("%d",&n);

a=(float *)realloc(a,n*sizeof (float));

if (!a) printf("\n oschibka");

else {printf("\nmassiw\n");

   for(int i=0;i<n;i++)

printf("%5.1f ",a[i]);

printf("\nwwedite elementi\n");

for(int i=0;i<n;i++)

scanf("%f",&a[i]);

       printf("\nmassiw\n");

   for(int i=0;i<n;i++)

printf("%5.1f ",a[i]);

   printf("\nwwedite nowoe kol-wo elementow massiwa\n");

       m=n;

   scanf("%d",&n);

       a=(float *)realloc(a,n*sizeof (float));

       printf("\nwwedite nowie elementi\n");

       for (int i=m;i<n;i++)

     scanf("%f",&a[i]);

   printf("\nmassiw\n");

     for(int i=0;i<n;i++)

   printf("%5.1f ",a[i]);

}     

a=(float *)realloc(a,0);

getch();

return 0;

}

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

Для освобождения ранее выделенного блока памяти используется функция free(). прототип функции void free(void *ptr).  Эта функция освобождает (возвращает в динамическую область) блок памяти, адресуемый указателем ptr. После этого освободившийся блок памяти становится доступным для выделения в будущем. Необходимо следить за тем, чтобы функция free() использовалась только с указателем, который был получен при работе с одной из рассмотренных функций динамического выделения памяти. Использование другого указателя может вызвать нарушение работы механизма управления памятью. При передаче нулевого указателя функция не выполняет никаких действий.

При использовании операции new необходимо указать тип элементов и количество элементов: int n=40; float *a; a=new float[n];. В соответствии с приведенным оператором в динамической памяти выделяется область для размещения 40 элементов типа float, адрес начала области помещается в указатель a. При использовании операции new не требуется выполнять преобразование типа указателя, так как она возвращает указатель в соответствии с типом элементов. Для освобождения динамической памяти в этом случае используется операция delete a[]. Размерность массива здесь не указывается, но скобки обязательны. При выделении и освобождении памяти необходимо следить за соответствием способов выполнения этих операций.

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

// работа с одномерным динамическим массивом

 

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

void input(float **a,int *n);

void output(float *a,int n);

void preob(float *a,int n);

int _tmain(int argc, _TCHAR* argv[])

{ float *a;

int n;

input(&a,&n);

printf("\nisxodnij massiw\n");

output(a,n);

preob(a,n);

printf("\npreobrasowannij massiw\n");

output(a,n);

getch();

delete a;

return 0;

}

void input(float **a,int *n)

{printf("wwedite kol-wo elementow massiwa\n");

scanf("%d",n);

*a=new float[*n];

printf("\nwwedite elementi\n");

for(int i=0;i<*n;i++)

scanf("%f",&(*a)[i]);

}

void output(float *a,int n)

{ for (int i=0;i<n;i++)

printf("%5.1f ",a[i]);

}

void preob(float *a,int n)

{float max,min;

 max=a[0]; min=a[0];

 for (int i=1;i<n;i++)

 if (a[i]>max) max=a[i];

 else if(a[i]<min) min=a[i];

 float y=max/min;

 for (int i=0;i<n;i++)

 a[i]*=y;

}

 

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

// вычитание двух треугольных динамических матриц

 

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

void input(float ***a,int *n);

void output(float **a,int n);

void preob(float **a,float **b,float ***c,int n);

int _tmain(int argc, _TCHAR* argv[])

{ float **a,**b,**c;

int n,m;

input(&a,&n);

input(&b,&m);

printf("\nmatriza a\n");

output(a,n);

printf("\nmatriza b\n");

output(b,m);

if (n==m)

{

preob(a,b,&c,n);

printf("\nmatriza c\n");

output(c,n);

}

getch();

return 0;

//функция ввода количества строк динамической матрицы и свмой матрицы

void input(float ***a,int *n)

{printf("wwedite kol-wo strok matrizi\n");

scanf("%d",n);

*a=new float *[*n];//выделение памяти для массива указателей на строки

                 // матрицы

for (int i=0;i<*n;i++)

(*a)[i]=new float [i+1];// выделение памяти для каждой строки матрицы

printf("\nwwedite elementi\n");

for(int i=0;i<*n;i++)

       for (int j=0; j<=i;j++)

scanf("%f",&(*a)[i][j]);

}

void output(float **a,int n)

{ for (int i=0;i<n;i++)

{

for (int j=0; j<=i;j++)

  printf("%5.1f ",a[i][j]);

printf("\n");

}

}

void preob(float **a,float **b,float ***c,int n)

{*c=new float *[n]; //выделение памяти для массива указателей на строки новой  

               //матрицы-результата

for (int i=0;i<n;i++) //выделение памяти для каждой строки матрицы-

                    //результата

(*c)[i]=new float [i+1];

for (int i=0;i<n;i++)

for (int j=0; j<=i;j++)

(*c)[i][j]=a[i][j]-b[i][j];

}

 

Перегружаемые функции

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

//перегружаемые функции

 

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

 

 float divide(float a,float b);

 int divide(int a,int b);

int _tmain(int argc, _TCHAR* argv[])

{ float a,b,c;

int k,l,m;

printf("wwedite dwa chisla float\n");

scanf("%f%f",&a,&b);

c= divide(a,b);

printf("\nwwedite dwa chisla int\n");

scanf("%d%d",&k,&l);

m=divide(k,l);

printf("\n c=%6.2f m=%4d",c,m);

getch();

return 0;

}

float divide(float a,float b)

{

return a/b;

}

int divide(int a,int b)

 {

return a/b;

 }

 

При вводе a=3 и b=2 будет вызвана функция, выполняющая деление вещественных чисел, так как a и b – вещественные переменные, и будет выведено c = 1.5. При вводе k=3 и n=2 будет вызвана функция, выполняющая деление целых чисел, так как k и n – переменные целого типа, и будет выведено m=1. 


Дата добавления: 2018-10-27; просмотров: 227; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!