Главная страница

Лекции Булатицкий Дмитрий Иванович (во многом по материалам Прасолова А. Н.)


Скачать 319.62 Kb.
НазваниеЛекции Булатицкий Дмитрий Иванович (во многом по материалам Прасолова А. Н.)
Дата11.01.2022
Размер319.62 Kb.
Формат файлаdocx
Имя файлаLecture_Programming_2021_09_01.docx
ТипЛекции
#328427
страница25 из 36
1   ...   21   22   23   24   25   26   27   28   ...   36

Механизм вызова функций в Си

  • Передача параметров в функцию


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

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

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

    #include
    double Sum(double A[], int nA)

    {

    double s = 0;

    while(nA) s += A[--nA];

    return s;

    }

    void main (void)

    {

    double B[] = { 1, 2, 3, 4, 5 };

    int nB = sizeof(B)/sizeof(B[0]);

    printf("Сумма = %lf\n", Sum(B,nB));

    printf("nB = %d\n", nB);

    }

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

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

    double Sum(double A[], int nA)

    {

    double s = 0, *Aend = A + nA;

    while( A < Aend ) s += *(A++);

    return s;

    }

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

    Вообще говоря, для формальных параметров-массивов описания в виде A[] и *A совершенно идентичны и обозначают локальную копию адреса соответствующего типа. Какое из этих описаний использовать, зависит от смысла параметра. Если это массив, то более наглядно использовать описание вида A[].
      1. Возврат значений


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

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

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

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

    {

    int i;

    for (i=0; i

    }
    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

    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   ...   21   22   23   24   25   26   27   28   ...   36


  • написать администратору сайта