Урок 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

 

рассказать друзьям и получить подарок

Оставить комментарий

Ваш email не будет опубликован. Обязательные поля отмечены *

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Translate Переводчик

Подписка на новости

SmartResponder.ru
Ваш e-mail: *
Ваше имя: *

Хостинг для Wordpress сайтов