Урок 9.4. Аргументы функции
9.4.1. Передача аргументов и возврат значений
9.4.2. Передача аргументов по умолчанию
9.4.3. Указатели в качестве аргументов
9.4.4. Передача аргументов функции по ссылке
9.4.5. Функции в качестве аргументов других функций
9.4.6. Массивы в качестве аргументов
9.4.7. Аргументы функции main()
9.4.1. Передача аргументов и возврат значений
Определяя некоторую функцию, мы описываем и ее параметры, которые называются "формальными" аргументами. Фактически – это новые объекты программы, и при вызове функции для них выделяется отдельная область памяти. Такими объектами могут быть константы, переменные, массивы.
При вызове некоторой функции в качестве списка параметров выступают фактические аргументы, значение которых вычисляется и присваивается формальным аргументам. Фактический аргумент может быть константой, переменной и более сложным выражением. Независимо от типа фактического аргумента, он вначале вычисляется, а затем его величина передается функции. Такая передача аргументов (фактических параметров) называется передачей по значению.
Определим функцию spaсe(), которая оставляет при печати задаваемое количество пробелов:
void space(int number) { int count; for(count=1;count<=number;count++) putchar(' '); } Приведем несколько примеров возможных вызовов этой функции: space(25); // функция использует в качестве аргумента константу int spaces; spaces=25; space(spaces); // аргументом является переменная //аргументом является выражение space((37-strlen("Examples of calls"))/2); |
где strlen() – стандартная функция определения длины строки. Передача значения из вызванной функции в вызывающую происходит с помощью оператора возврата.
Возврат значения в вызывающую функцию осуществляется оператором return; или return(<выражение>);. Например, функция, которая вычисляет абсолютную величину числа, может быть описана так:
int abs(int x) { if(x<0) x =- x; return(x); } |
Возвращаемое значение можно присвоить переменной или использовать как часть некоторого выражения. Например, y = abs(x); или y = 2*abs(x)+3;
Задание. Перепишите функцию abs() так, чтобы ее тело состояло из одного оператора.
Значение выражения в return(<выражение>) всегда преобразуется к типу функции, и только после этого следует возврат.
Оператор return завершает выполнение вызываемой функции и передает управление вызывающей функции (оператору, следующему за вызовом). Это происходит даже в том случае, если оператор return является не последним оператором тела функции.
Для завершения выполнения функции можно использовать просто оператор return;. Поскольку у данного оператора отсутствует выражение в скобках, никакое значение при этом не передается вызывающей функции.
Управление возвращается в вызывающую функцию и в случае выхода по достижению конца функции. Значение при этом не передается.
Описание типа возвращаемого значения. Тип функции определяется типом возвращаемого ею значения, а не типом ее аргументов. Функция, которая не возвращает значения, должна быть описана как имеющая тип void. Например,
void ErrorMsg(char *s)/* функция не возвращает значения */
{
printf("==>%s\n",s);
}
Если указание типа отсутствует, то по умолчанию предполагается, что функция имеет тип int. В языке С++ не поддерживается тип функции int по умолчанию.
9.4.2. Передача аргументов по умолчанию
При объявлении функции в С++ одному или нескольким аргументам может быть назначено значение по умолчанию. Это означает, что при вызове, аргументы имеющие значения по умолчанию, можно опускать.
Например:
void func(double m, char ch = ’*’, int i = 2);
Два параметра – ch и i имеют значения по умолчанию, которые будут использованы в случае вызова функции func с неполным списком параметров:
func(6.78, ’d’, 5);
func(6.78, ’d’);
func(6.78);
Вызов
func(6.78, 22);
будет неправильным, т.к. второй параметр должен иметь тип char.
При объявлении функции имена формальных параметров указывать необязательно, поэтому функцию можно объявит и так:
void func(double, char = ’*’, int = 2);
Если вы не указываете имена параметров, то надо быть осторожным при инициализации параметров указателей и ссылок. Например, в объявлении
void f(double, char * = ”Hello”, int = 2);
если “забыть” пробел перед знаком =, то можно получить составную операцию умножения: *=;
Параметры со значениями по умолчанию должны быть последними в списке параметров. Кроме того, к моменту вызова функции с неполным списком значение по умолчанию уже должно быть определено и видимо, потому что значения по умолчанию могут быть заданы либо в объявлении, либо в определении, но лишь в одном месте. Хороший стиль программирования требует задавать параметры по умолчанию в прототипе функции.
9.4.3. Указатели в качестве аргументов
С помощью оператора return в вызывающую функцию можно передать только одну величину. А как вернуть две или более величины? Для этого нужно воспользоваться переменными типа указатель и передавать не данные, а их адреса или ссылки. Передача параметров по ссылке рассматривается в следующем разделе.
Пусть, например, мы хотим с помощью некоторой функции swap() выполнить обмен значений у переменных х и y. Ясно, что мы должны передать этой функции в качестве аргументов две величины и получить в качестве результата тоже две величины. Если мы определим функцию swap() таким образом:
void swap(int u, int v) { int temp; temp=u; u=v; v=temp; } |
и обратимся к ней с аргументами х,у: swap(x, y); то произойдет передача параметров по значению, т.е. переменная u получит значение из переменной х, а v – из переменной у, функция выполнит обмен значений над переменными u и v, которые существуют только в этой функции и вне её недоступны. Следовательно, в результате вызова swap(x, y) переменные х и у не изменяют своих значений.
Рассмотрим, как следует воспользоваться указателями для возврата из функций нескольких значений. Определим нашу функцию следующим образом:
void swap(int *u, int *v) // u и v являются указателями { int temp; temp = *u; // temp получает значение, на которое указывает u */ *u = *v; *v = temp; } //Тогда вызов этой функции будет оформлен так: int x,y; swap(&x, &y); // передача адресов |
Посмотрим, как работает теперь эта функция. В этом случае вместо передачи значений x и y мы передаем их адреса. Это значит, что формальные аргументы u и v при обращении будут заменены адресами, и, следовательно, они должны быть описаны как указатели. Поскольку x и y – целого типа, u и v являются указателями на переменную целого типа, и они описаны так: int *u,*v;
Далее рассмотрим оператор temp = *u;
Переменная temp целого типа и используется для временного хранения обмениваемых величин.
Значение переменной u – это &x (следует из вызова функции), поэтому переменная и является указателем на x. Значит, операция * и дает значение х. (Если бы мы написали temp = u;, то произошло бы запоминание адреса переменной x, а не ее значения.) Точно так же, желая присвоить переменной y значение переменной x, мы пользуемся оператором *u = *v; который соответствует оператору x = y ;
Итак, путем передачи функции адресов переменных x и y мы предоставили ей возможность доступа к ним. Используя указатели и операцию *(разадресации), функция смогла извлечь величину, помещенную в соответствующие ячейки памяти, и поменять их местами.
Обратите внимание! При вызове функции информация о переменной может передаваться функции в двух видах. Если мы используем форму обращения function1(x); происходит передача значений переменной x. Если же мы используем форму обращения function2(&x); происходит передача адреса переменной x. Первая форма обращения требует, чтобы определение функции включало в себя формальный аргумент того же типа, что и x: function1(int num);
Вторая форма обращения требует, чтобы определение функции включало в себя формальный аргумент, являющийся указателем на объект соответствующего типа: function2(int *ptr).
Пользуйтесь первой формой, если входное значение необходимо функции для некоторых вычислений или действий, значение будет копироваться в функцию и вызывающей функции не изменяется. Пользуйтесь второй формой, если функция должна будет изменить значение переменных в вызывающей программе, т.е. по месту их нахождения, сами значения в функцию копироваться не будут.
Таким образом, передача указателей на данные вместо самих данных может быть использована в следующих случаях:
- для передачи в функцию больших объемов (структур) данных, чтобы избежать копирования аргументов в стек;
- для передачи в функцию аргументов, которые будут использованы не для передачи данных, а для возврата значений из функции, что позволяет вернуть из функции более одного значения;
- для передачи в функцию адресов массивов (см. § 9.4.6. Массивы в качестве аргументов);
9.4.4. Передача аргументов функции по ссылке
В С++ есть возможность передачи аргументов в функцию по ссылке. Случаи передачи ссылок аналогичны случаям передачи указателей в функции:
- передача в функцию больших объемов (структур) данных, в т.ч. объектов, чтобы избежать копирования аргумента в стек;
- для передачи в функцию аргументов, которые должны быть изменены функцией; т.е. фактически для возврата значений из функции, что позволяет вернуть из функции более одного значения;
У ссылок есть преимущества перед указателями: нет необходимости выполнять операцию разадресации, т. к. ссылка является уже разадресованным указателем. Например, функция обмена местами двух чисел из предыдущего параграфа с использованием ссылок выглядит следующим образом:
void swap(int &u, int &v) // u и v являются ссылками { int temp; temp=u; u=v; v=temp; } //Тогда вызов этой функции будет оформлен так: swap(a,b); //передача ссылок |
9.4.5. Функции в качестве аргументов других функций
Рассмотрим пример, в котором имена функций, являющиеся по сути адресами функций, служат параметрами другой функции – функции fn_call(). В этой функции первый параметр является указателем на функцию.
#include <iostream> #include <conio.h> using namespace std; double abs1(double x) { return x >= 0.0 ? x : -x; } double abs2(double x) { return x < 0.0 ? -x : x; } double fn_call(double (*f)(double), double x) //параметр f имеет тип указатель на функцию //с одним параметром типа double и возвращающую //значение типа double { return f(x); } void main() { cout << "x = " << fn_call(abs1,-2) << endl << "x = " << fn_call(abs2,-11); _getch(); } |
Как видно из программы, функции abs1 и abs2 вызываются не напрямую, а через функцию fn_call, которой передаются имена (указатели) этих функций.
Рассмотрим пример более замысловатой программы, в которой адрес функции передается как параметр другой функции. Пример искусственен, но полезен для изучения технологии передачи параметров в функции.
В программе сравниваются две строки в лексикографическом порядке (функция strcmp) и обмениваются местами (функция swap), если они расположены по возрастанию. Вызов функций strcmp и swap происходит через указатель на функцию и размещен в функции example, вызов последней происходит из функции main:
// Вариант для языка C (файл .c) #include <stdio.h> #include <conio.h> int swap(); int example(); int stringcmp(); void main() { char *a = "aaaaysa1", *b = "aaaaysa"; clrscr(); printf("%s \n%s \n\n", a,b); //косвенные вызовы функций stringcmp и swap через функцию example if ( example(a,b,stringcmp ) > 0 ) example(&a,&b,swap); printf("%s \n%s \n\n", a,b); getchar(); } int example(char x[],char y[],int (*exch)()) //exch - указатель на функцию, возвращающей целое значение*/ { return(exch(x,y)); //return((*exch)(x,y)); } int stringcmp(char *s, char *t) // возвращает <0,если s < t; // 0,если s = t; // >0,если s > t { for(;*s == *t && *s!=0 && *t!= 0; s++,t++); return(*s-*t); } int swap(char **pa, char **pb) //меняет местами значения указателей { char *temp; temp = *pa; *pa = *pb; *pb = temp; } // Вариант для языка CPP (файл .cpp): #include <stdio.h> #include <conio.h> int swap(char **px, char **py); int example(char x[], char y[], int (*exch)(char *s, char *t)); int stringcmp(char *s, char *t); typedef int FPT (char *s, char *); void main() { clrscr(); char *a = "sssbb", *b = "sssa"; printf("\nDo\n"); printf("%s\n%s\n",a,b); if ( example(a,b,stringcmp ) > 0 ) //example(&a,&b,(FPT)swap); swap(&a, &b); printf("\nPosle\n"); printf("%s\n%s\n",a,b); getchar(); } int example(char x[],char y[],int (*exch)(char *s, char *t )) //exch - указатель на функцию, возвращающей целое значение { return(exch(x,y)); //return((*exch)(x,y)); } int stringcmp(char *s, char *t) // возвращает <0, если s < t; // 0, если s = t; // >0, если s > t { for(;*s == *t && *s != 0 && *t != 0; s++,t++); return(*s - *t); } int swap(char **pa, char **pb) //меняет местами значения указателей { char *temp; temp =* pa; *pa =* pb; *pb = temp; } |
Описание int(*exch)(); говорит о том, что exch – указатель на функцию, вызывающую целое значение. Первая пара скобок необходима, без них int *exch(); означало бы, что exch – функция, возвращающая ссылку на целое значение, а это совершенно разные вещи.
9.4.6. Массивы в качестве аргументов
9.4.7. Аргументы функции main()
В языке Си существует возможность передавать программе в начале ее выполнения некоторые аргументы из командной строки, которая набирается на клавиатуре при запуске этой программы.
Аргументы в командной строке являются дополнительными элементами в той же самой строке. Аргументы передаются функции main().
Компилятор Си предполагает наличие у main() трех аргументов. Первый аргумент – количество символьных строк, разделенных пробелами, в командной строке. Обычно, но не обязательно, этот аргумент типа int называется argc (от слов ARGument Count). Второй аргумент является массивом указателей символьных строк. Каждой символьной строке, входящей в командную строку, присваивается собственный указатель.
Если функции main() передается третий параметр, то этот параметр будет указывать на массив указателей на строки, определяющие операционную среду, в которой выполняется программа.
Ниже приводится пример передачи программе аргументов из командной строки. В командной строке было набрано:
>C:\BC\OUTPUT\exemain.exe str1 str2
main(int argcnt, char *argv[], char *envpr[]) // программа печати аргументов главной программы // argcnt; – количество аргументов // *argv[]; – массив указателей на аргументы // *envpr[];– массив указателей на строки, определяющие операционную среду { int i; printf("Количество аргументов: %d \n", argcnt); for(i=0; i < argcnt; i++) printf("Аргумент %d %s \n",i, argv[i]); printf("\nОперационная среда:"); while(*envpr) printf("\n%s",*envpr++); } /*Результат работы программы:*/ Количество аргументов: 3 Аргумент 0 C:\TC\OUTPUT\EXMAIN.EXE Аргумент 1 str1 Аргумент 2 str2 Операционная среда: COMSPEC=C:\COMMAND.COM PROMPT=$p$g PATH=C:\;C:\SYS;C:\DOS;C:\TC;C:\MASM;C:\NC;C:\DOS\ALLARC; NC=c:\nc |
Оставить комментарий