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


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



ЗНАЕТЕ ЛИ ВЫ?

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



При динамическом распределении памяти для массивов следует описать соответствующий указатель и

присваивать ему значение при помощи функции calloc. Одномерный массив a[10] из элементов типа

float можно создать следующим образом

      float *a;

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

Для создания двумерного массива вначале нужно распределить память для массива указателей на одномерные массивы, а затем распределять память для одномерных массивов. Пусть, например, требуется создать массив a[n][m], это можно сделать при помощи следующего фрагмента программы:

 

      #include

      main ()

      {

                          double **a;

                          int n,m,i;

                          scanf("%d %d",&n,&m);

                          a=(double **)calloc(m,sizeof(double *));

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

                          a[i]=(double *)calloc(n,sizeof(double));

                                                ............

      }

 

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

 

      #include

      main ()

      {

                          long ***a;

                          int n,m,l,i,j;

                          scanf("%d %d %d",&n,&m,&l);

                          /* -------- распределение памяти -------- */

                          a=(long ***)calloc(m,sizeof(long **));

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

                          {

                                               a[i]=(long **)calloc(n,sizeof(long *));

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

                                               a[i][j]=(long *)calloc(l,sizeof(long));

                          }

                                                                  ............

                          /* --------- освобождение памяти ----------*/

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

                          {

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

                                               free (a[i][j]);

                                               free (a[i]);

                          }

                           free (a);

      }

 

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

 

Пример:

 

      #include

      main()

      {

                          int vvod(double ***, long **);

                          double **a; /* указатель для массива a[n][m] */

                          long *b;   /* указатель для массива b[n] */

                          vvod (&a,&b);

                           .. /* в функцию vvod передаются адреса указателей, */

                           .. /* а не их значения                        */

                           ..

      }

      int vvod(double ***a, long **b)

      {

                          int n,m,i,j;

                          scanf (" %d %d ",&n,&m);

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

                          *b=(long *)calloc(n,sizeof(long));

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

                          *a[i]=(double *)calloc(m,sizeof(double));

                           ..... }

       

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


 

Функции. Определения функций. Оператор return.

Функция – это совокупность объявлений и операторов, обычно предназначенная для решения определенной задачи. Каждая функция должна иметь имя, которое используется для ее объявления, определения и вызова. В любой программе на С должна быть функция с именем main (главная функция), именно с этой функции, в каком бы месте программы она не находилась, начинается выполнение программы.

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

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

Определения функций

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

В соответствии с синтаксисом языка С определение функции имеет следующую форму:

[спецификатор-класса-памяти] [спецификатор-типа] имя-функции

([список-формальных-параметров])

{ тело-функции }

  

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

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

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

Пример программы, возвращающий сумму двух целых величин:

      #include < stdio.h >

      int sum(int a, int b);

      int main()

      {

                          int a = 2, b = 3, c, d;

                          c = sum(a,b);

                          scanf("%d", d);

                          printf("%d", sum(c,d));

                          return 0;

      }

      int sum(int a, int b);

      {

                          return(a+b);

      }

Функция возвращает значение, если ее выполнение заканчивается оператором

               return [выражение];

Указанное выражение вычисляется, преобразуется, если необходимо, к типу возвращаемого значения и возвращается в точку вызова функции в качестве результата. Если оператор return не содержит выражения или выполнение функции завершается после выполнения последнего ее оператора (без выполнения оператора return), то возвращаемое значение не определено. Для функций, не использующих возвращаемое значение, должен быть использован тип void, указывающий на отсутствие возвращаемого значения. Если функция определена как функция, возвращающая некоторое значение, а в операторе return при выходе из нее отсутствует выражение, то поведение вызывающей функции после передачи ей управления может быть непредсказуемым.

Пример:

               int f1(){return 1;} // эта запись правильная

               void f2(){return 1;} // неправильно, f2 не должна возвращать значение

               double f3(){return 1;} //правильно: 1 преобразуется к типу double

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

Пример:

               int *f()

               {

                                    int a = 5;

                                    return &a; //нельзя

               }


 

Функции. Прототипы функций.

Функция – это совокупность объявлений и операторов, обычно предназначенная для решения определенной задачи. Каждая функция должна иметь имя, которое используется для ее объявления, определения и вызова. В любой программе на С должна быть функция с именем main (главная функция), именно с этой функции, в каком бы месте программы она не находилась, начинается выполнение программы.

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

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

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

               int func(int a, float b, char*c)

                                                                            или

               int func(int, float, char*)

Два этих прототипа являются равносильными.

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

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

Таким образом, прототип функции необходимо задавать в следующих случаях:

               1. Функция возвращает значение типа, отличного от int.

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

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

В прототипе можно указать, что число параметров функции переменно, или что функция не имеет параметров.

Если прототип задан с классом памяти static, то и определение функции должно иметь класс памяти static. Если спецификатор класса памяти не указан, то подразумевается класс памяти extern.

Пример:

               float sqr(float a); // прототип функции sqr

               main()

               {

                                    float b;

                                    b = 5.2;

                                    // вызов функции sqr по прототипу

                                    Printf("Квадрат числа ", b, "= %d", sqr(b));

                                    getch();

                                    return 0;

               }

               // определение функции, описание ее действий

               float sqr(float a)

               {

                                    return a*a;

               }


 



Поделиться:


Последнее изменение этой страницы: 2021-08-16; просмотров: 50; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.15.147.215 (0.032 с.)