Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

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




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

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

 

Таблица 9

Функции выделения и освобождения памяти

 

Функция Прототип и краткое описание
malloc void *malloc(unsigned s); Возвращает указатель на начало области (блока) динамической памяти длиной в s байт. При неудачном завершении возвращает значение NULL.
calloc void *calloc(unsigned n, unsigned m); Возвращает указатель на начало области (блока) обнуленной динамической памяти, выделенной для размещения n элементов по m байт каждый. При неудачном завершении возвращает значение NULL.
realloc void *realloc(void *bl, unsigned ns); Изменяет размер блока ранее выделенной динамической памяти до размера ns байт, bl – адрес начала изменяемого блока. Если bl равен NULL(память не выделялась), то функция выполняется как malloc. При неудачном завершении возвращает значение NULL.
free void *free(void *bl); Освобождает ранее выделенный участок (блок) динамической памяти, адрес первого байта которого равен bl.

 

Примеры [программа pr_20]:

1. Выделение памяти под одномерный массив

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <alloc.h>

int main(void)

{

int *array, n, i;

clrscr();

printf("Enter n=");

scanf("%d", &n);

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

if(!array)

{

printf("ERROR! Press any key... ");

getch();

return 1;

}

printf(" Array: ");

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

{

array[i] = i;

printf("%5d", array[i]);

}

printf(" Press any key...");

getch();

free(array);

return 0;

}

 

2. Выделение памяти под двумерный массив

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <alloc.h>

int main(void)

{

int **array, n, i, j;

clrscr();

printf("Enter n=");

scanf("%d", &n);

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

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

array[i] = (int*) malloc(n*sizeof(int*));

if(!array)

{

printf("ERROR! Press any key... ");

getch();

return 1;

}

printf(" Array: ");

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

{

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

{

array[i][j] = i+j;

printf("%5d", array[i][j]);

}

printf(" ");

}

printf(" Press any key...");

getch();

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

free(array[i]);

free(array);

return 0;

}

 

Лекция № 11

 

Написал Lekka
07.11.2008
Функции О функциях в языке Си нужно говорить, рассматривая это понятие с двух сторон. Во-первых, это один из производных типов, во-вторых, функция – это минимальный исполняемый модуль программы. Основная форма описания функции имеет вид: тип_возвращаемого_значения имя_функции (список_формальных_параметров) { тело_функции; } Здесь: тип_возвращаемого_значения имя_функции (список_формальных_параметров) – заголовок функции; тип_возвращаемого_значения определяет тип значения, которое возвращает функция. Если тип не указан, то по умолчанию предполагается, что функция возвращает целое значение (тип int). Если тип void, то функция не возвращает значение. имя_функции – произвольно выбираемый идентификатор, не совпадающий со служебными словами и с именами других объектов программы. список_формальных_параметров состоит из перечня типов и имен параметров, разделенных запятыми. В списке параметров для каждого параметра должен быть указан тип. Функция может не иметь параметров, тогда в скобках записывают void или оставляют их пустыми, но круглые скобки обязательны. тело_функции – это часть определения функции, ограниченная фигурными скобками и непосредственно размещенная вслед за заголовком функции. Тело функции может быть либо составным оператором, либо блоком (в отличие от составного оператора блок включает определения объектов). Обязательным, но не всегда явно используемым оператором тела функции является оператор возврата из функции return, имеющий две формы: return; - эта форма соответствует завершению функции, не возвращающая никакого значения. return выражение; - эта форма соответствует возвращению некоторого значения, соответствующее типу, указанному перед именем функции в ее описании, либо иметь тип, допускающий автоматическое преобразование к типу возвращаемого функцией значения.   Пример [программы pr_21, pr_22, pr_23]:
void f1(void) { printf(“Error! Press any key…”); getch(); return; } void f2(int x, int y) { if(x>y) printf(“X=%d Press any key…”, x); else printf(“Y=%d Press any key…”, y); getch(); return; }
int f3(void) { int x; printf(“x=”); scanf(“%d”, &x); return x*2; } int f4(int x, int y) { if(y) return x/y; return 0; }

 

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

тип_возвращаемого_значения имя_функции (спецификация_формальных_параметров);

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

 

Пример:

void f1(void); void f2(int, int);
int f3(void); int f4(int, int);

Для обращения к функции используется выражение с операцией «круглые скобки»:

имя_функции (список_фактических_параметров);

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

Соответствие между формальными и фактическими параметрами устанавливается по их взаимному расположению в списках. Между формальными и фактическими параметрами должно быть соответствие по типам.

Синтаксис языка Си предусматривает только один способ передачи параметров – передачу по значениям. Это означает, что никакие операции над формальными параметрами в теле функции не изменяют значения фактических параметров.

Передача параметров по значению предусматривает следующие шаги:

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

2. вычисляются значения выражений, использованных в качестве фактических параметров при вызове функции.

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

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

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

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

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

 

Пример [программа pr_24]:

#include<stdio.h>

#include<conio.h>

void swap1(int, int);

void swap2(int*, int*);

int main(void)

{

int x=5, y=10;

clrscr();

swap1(x, y);

printf(“X=%d Y=%d ”, x, y);

swap2(&x, &y);

printf(“X=%d Y=%d ”, x, y);

getch();

return 0;

}

void swap1(int a, int b)

{

int tmp=a; a=b; b=tmp;

}

void swap2(int *a, int *b)

{

int tmp=*a; *a=*b; *b=tmp;

}

 

Результат:

После выполнения функции swap1():

X=5 Y=10

После выполнения функции swap2():

X=10 Y=5

 

Если в качестве аргумента функции используется обозначение массива, есть лишь один способ – передача адреса начала массива. Сделать это можно двумя способами:

void input(int array[], int n); или void input(int *array, int n);

Конструкции int array[] и float * array совершенно равноправны в спецификациях параметров. В теле функции обращение к элементам массивов-параметров выполнялось с помощью индексированных элементовarray[i] или используя операцию разыменования *(array+i).

 

Пример [программа pr_25, pr_26]:

Одномерные массивы Двумерные массивы
Способ 1
void input(int *m, int n); void output(int *m, int n); int main(void) { int mas[10]; clrscr(); randomize(); printf(“Array: "); input(&mas[0], 10); output(&mas[0], 10); getch(); return 0; } void input(int *m, int n) { int i; for(i=0;i<n;i++) *(m+i) = -10 + random(21); } void output(int *m, int n) { int i; for(i=0;i<n;i++) printf("%4d", *(m+i)); printf(" "); } void input(int *m, int n); void output(int *m, int n, int k); int main(void) { int mas[10][7]; clrscr(); randomize(); printf(“Array: "); input(&mas[0][0], 10*7); output(&mas[0][0], 10, 7); getch(); return 0; } void input(int *m, int n) { int i; for(i=0;i<n;i++) *(m+i) = -10 + random(21); } void output(int *m, int n, int k) { int i, j; for(i=0;i<n;i++) { for(j=0;j<k;j++) printf("%4d", *(m+i*k+j)); printf(" "); } }
Способ 2
void input(int m[], int n); void output(int m[], int n); int main(void) { int mas[10]; clrscr(); randomize(); printf(“Array: "); input(&mas[0], 10); output(&mas[0], 10); getch(); return 0; } void input(int m[], int n) { int i; for(i=0;i<n;i++) m[i] = -10 + random(21); } void output(int m[], int n) { int i; for(i=0;i<n;i++) printf("%4d", m[i]); printf(" "); } Не существует

 




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


Дата добавления: 2014-01-20; Просмотров: 592; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.088 сек.