Язык С

       

Указатели и аргументы функций


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

SWAP(A, B);

определив функцию SWAP при этом следующим образом:

SWAP(X, Y) /* WRONG */ INT X, Y; { INT TEMP;

TEMP = X; X = Y; Y = TEMP; }

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

SWAP(&A, &B); так как операция & выдает адрес переменной, то &A является указателем на A. В самой SWAP аргументы описываются как ука- затели и доступ к фактическим операндам осуществляется через них.

SWAP(PX, PY) /* INTERCHANGE *PX AND *PY */ INT *PX, *PY; { INT TEMP;

TEMP = *PX; *PX = *PY; *PY = TEMP; }

Указатели в качестве аргументов обычно используются в функциях, которые должны возвращать более одного значения. (Можно сказать, что SWAP вOзвращает два значения, новые зна- чения ее аргументов). В качестве примера рассмотрим функцию GETINT, которая осуществляет преобразование поступающих в своболном формате данных, разделяя поток символов на целые значения, по одному целому за одно обращение. Функция GETINT должна возвращать либо найденное значение, либо признак кон- ца файла, если входные данные полностью исчерпаны. Эти зна- чения должны возвращаться как отдельные объекты, какое бы значение ни использовалось для EOF, даже если это значение вводимого целого. Одно из решений, основывающееся на описываемой в главе 7 функции ввода SCANF, состоит в том, чтобы при выходе на ко- нец файла GETINT возвращала EOF в качестве значения функции; любое другое возвращенное значение говорит о нахождении нор- мального целого. Численное же значение найденного целого возвращается через аргумент, который должен быть указателем целого. Эта организация разделяет статус конца файла и чис- ленные значения. Следующий цикл заполняет массив целыми с помощью обраще- ний к функции GETINT:


INT N, V, ARRAY[SIZE];

FOR (N = 0; N < SIZE && GETINT(&V) != EOF; N++) ARRAY[N] = V;

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

Сама GETINT является очевидной модификацией написанной нами ранее функции ATOI:

GETINT(PN) /* GET NEXT INTEGER FROM INPUT */ INT *PN; { INT C,SIGN;

WHILE ((C = GETCH()) == ' ' \!\! C == '\N' \!\! C == '\T'); /* SKIP WHITE SPACE */ SIGN = 1; IF (C == '+' \!\! C == '-') { /* RECORD SIGN */ SIGN = (C == '+') ? 1 : -1; C = GETCH(); } FOR (*PN = 0; C >= '0' && C <= '9'; C = GETCH()) *PN = 10 * *PN + C - '0'; *PN *= SIGN; IF (C != EOF) UNGETCH(C); RETURN(C); }

Выражение *PN используется всюду в GETINT как обычная пере- менная типа INT. Мы также использовали функции GETCH и UNGETCH (описанные в главе 4) , так что один лишний символ, кототрый приходится считывать, может быть помещен обратно во ввод.

Упражнение 5-1

--------------- Напишите функцию GETFLOAT, аналог GETINT для чисел с плавающей точкой. Какой тип должна возвращать GETFLOAT в ка- честве значения функции?




    Содержание раздела