Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на C / C++ / Язык программирования Си++. Лекции.DOC
Скачиваний:
173
Добавлен:
02.05.2014
Размер:
775.17 Кб
Скачать
    1. Возврат значений из функций

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

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

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

void FillArray(double A[], int nA, double val)

{

int i;

for (i=0; i<nA; i++) A[i] = val;

}

void main (void)

{

double B[100];

FillArray(B, 40, 35.4);

/* ... */

FillArray(&B[60], 20, 15.4);

/* ... */

}

Первый вызов FillArray() заполняет 40 первых элементов массива B значением 35.4, второй вызов заполняет 20 элементов массива B, начиная с элемента B[60], значением 15.4. При возврате из функции массив будет изменен, т. к. занесение значения val происходит непосредственно по нужному адресу.

Эту же функцию можно использовать для заполнения строк двумерного массива:

void main (void)

{

double a[10][20];

int n = sizeof(a) / sizeof(a[0]);

int m = sizeof(a[0]) / sizeof(a[0][0]);

int i;

/* ... */

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

FillArray(a[i],m, 14.6);

/* ... */

}

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

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

void Decart(double *px, double *py, double r, double f)

{

(*px) = r * cos(f);

(*py) = r * sin(f);

}

При обращении к данной функции для параметров px и py нужно передавать адреса:

void main(void)

{

double x, y, r=5, f=0.5;

/* ... */

Decart( &x, &y, r, f );

/* ... */

}

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

  1. Работа с динамической памятью

    1. Стандартные функции управления динамической памятью

Данные, которые создаются, инициализируются и уничтожаются по требованию программиста называются динамическими. Для управления такими данными используются специальные стандартные функции, прототипы которых описаны в заголовочном файле <malloc.h> (для некоторых компиляторов <alloc.h>).

Для запроса динамической памяти служит функция malloc(), которая имеет следующий прототип:

void * malloc(size_t size);

Функция malloc() выделяет область динамической памяти, размером size байт, и возвращает адрес этой области памяти.

Параметр size, имеет тип size_t, который описан в файле <malloc.h> с помощью оператора typedef и используется для описания размеров, счетчиков и т.д. Обычно тип size_t соответствует типу unsigned int.

В том случае, когда функция malloc() не может удовлетворить запрос на память, она возвращает значение NULL, то есть значение не существующего указателя. Константа NULL описана в заголовочном файле <malloc.h>. Значение NULL возвращается и в том случае, когда значение параметра size нулевое.

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

После того, как выполнена вся работа с выделенной областью памяти, ее следует освободить с помощью функции free(), имеющей следующий прототип:

void free(void *block);

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

Если при вызове функции free() значение указателя block не соответствует адресу, возвращенному функцией выделения памяти, то результат выполнения функции free() непредсказуем, а область динамической памяти может быть вообще разрушена.

Не допускается также освобождать уже освобожденный блок памяти.

Значение параметра block равное NULL не вызывает никаких действий со стороны функции free();

Рассмотрим типичную последовательность действий при работе с динамической памятью:

double *A; int n;

...

n = 200;

...

A = (double *) malloc( n * sizeof(double) );

...

/* Работа с массивом A */

...

free(A);

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

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

В некоторых случаях бывает полезной функция calloc(), которая не только выделяет память, но и заполняет область выделенной памяти нулевыми значениями. Она имеет следующий прототип:

void * calloc(size_t nitems, size_t size);

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

Функция realloc() служит для изменения размера ранее выделенного блока памяти:

void *realloc(void *block, size_t size);

Здесь block - адрес ранее выделенного блока памяти, size - новый размер блока в байтах. Функция возвращает значение нового указателя на блок памяти, которое может и не совпадать со старым.

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

Все рассмотренные функции могут выделять память размером не более одного сегмента, то есть не более 64K в 16-ти разрядных моделях и не более 4G в 32-х разрядных моделях памяти.

При работе с динамической памятью следует иметь в виду, что в каждом выделенном блоке несколько байт отводится на служебную информацию. Так в 16-ти разрядной Large модели память выделяется блоками по размеру кратными 16 байтам, и в каждом блоке 4 байта служебные.

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

Функция coreleft() возвращает значение оставшейся в динамической области памяти в байтах. Функция может иметь следующие прототипы в зависимости от моделей памяти:

unsigned coreleft(void); /* Маленьких модели */

unsigned long coreleft(void); /* Большие модели */

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

Соседние файлы в папке C++