Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
DOROGOVA.pdf
Скачиваний:
245
Добавлен:
05.06.2015
Размер:
853.4 Кб
Скачать

}

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

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

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

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

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

Часто фактические параметры функции называют её аргументами. При вызове функции количество,

порядок и типы фактических параметров должны соответствовать формальным параметрам в определении функции.

Можно выделить следующие этапы вызова функции:

Проверка соответствия фактических и формальных параметров функции.

Присваивание фактических значений формальным параметрам функции.

Передача управления на первый оператор функции.

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

Пример: Вывести на экран дату в формате Год: Месяц: День:

#include <stdio.h>

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

void dateprint (int gg , int mm , int dd )

{printf (“\n year: %d”,gg); printf (“\t month: %d”,mm); printf (“\t day: %d”,dd);

}

void main()

{

dateprint ( 1990 , 12 , 17) ; // вызов (Обращение к функции)

}

Результат выполнения:

 

Year: 1990

month: 12

day: 17

Функция не возвращает значения, поэтому её тип – void. При вызове происходит присваивание значений фактических параметров соответствующим формальным параметрам (gg=1090 mm=12 dd=17).

8.5.1. Передача параметров по значению

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

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

Пример: функция power() возводит base в n-ю степень. #define dec 25

int power(int base, int n)

{int p;

for (p = 1; n > 0; --n) p = p * base;

return p;

}

void main() { i,p=0;

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

for(i=0 ; i<5 ; i++) p=p+power(dec , i); ….

}

Обратите внимание на одноименные переменные p в вызывающей программе и функции power(), это две разные переменные - локальные переменные функций main() и power(). Переменная p в функции power() "маскирует" (или исключает) переменную p в вызывающей программе.

При вызове power(dec , i) создаются две локальные переменные для формальных параметров base и n, а также локальная переменная p. Формальному параметру base присваивается фактическое значение dec, а формальному параметру n - фактическое значение i (base=dec, n=i). Формальный параметр n является переменной для счета числа шагов цикла, что бы мы ни делали с n внутри функции power(), это не окажет никакого влияния на саму переменную i в вызывающей программе.

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

Пример:

// определение функции swap void swap (int x, int y)

{int t = x;

x= y;

y= t;

printf("swab: x=%d\t y=%d",x,y);

}

void main()

{int a = 3, b = 7; swap (a, b);

printf("main: a=%d\t b=%d",a,b);

}

Результат выполнения программы: swab: x=7 y=3

main: a=3 b=7

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

8.5.2. Указатель в качестве параметра функции

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

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

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

void swap (int* x, int* y)

{int t; t = *x;

*x = *y;

*y = t;

}

void main()

{ int a=3,b=7,*p1,*p2;

//вызов функции может происходить по-разному: // 1. передача адресов переменных

swab (&a , &b);

printf("1. main: a=%d\t b=%d",a,b);

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

// 2. передача указателей на переменные p1= &a;

p2= &b; swab (p1,p2);

printf("2.main: a=%d\t b=%d",a,b);

}

Результат выполнения программы:

1. main: a=7

b=3

2. main: a=3

b=7

Вновом исполнении функция swap() имеет два формальных параметра - указатели на переменные типа int с именами x и y.

При первом вызове функции (swap (&a, &b)) передаются значения адресов переменных a и b, которые записываются в локальные переменные-указатели x и y, то есть при вызове функции неявно выполняются присваивания x=&a и y=&b. Таким образом, сами переменные a и b становятся доступными из функции swap () через указатели x и y.

При втором вызове функции передаются указатели, инициированные адресами переменных a и b, то есть при вызове неявно выполняются присваивания x=p1 и y=p2.

Втеле функции для доступа к переменным через указатели используется операция разименования (*), инструкция *x = *y означает следующее : переменную, адресуемую через указатель y (b), записать в переменную, адресуемую через указатель x (a), а инструкция *y = t означает: переменную t записать в переменную, адресуемую через указатель y.

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

Пример: Сортировка массива по убыванию, определенного в вызывающей функции, main() #include <iostream.h>

#include <stdlib.h> #include <time.h>

#define n 100

// размер массива

#define col 10

// печать по 10 элементов в строке

void swab (int *p1 , int *p2)

{/* создание локальной переменнрой f и записть в нее значение из переменной указателя p1 */ int f=*p1;

*p1=*p2; *p2 =f;

}

void mas_print (int *p_beg, int *p_end, int k)

{ int i=0 ; // счетчик выведенных элементов for (i=0 ; p_beg < p_end ; i++)

{cout<<*p_beg++;

if ((i+1)%k==0) cout<<'\n'; else cout<<'\t';

}

}

void main()

{ int A[n],*p,i; // определение массива и указателя srand(time(0));

// формирование исходного массива for (p=A ; p<&A[n] ; p++) *p=rand();

mas_print(A , &A[n] , col); // печать массива до сортировки cout<<"\n!!! сортировка !!!!\n";

for (i=0 ; i<n ; i++) //______сортировка____

for( p=A ; p<&A[n-i-1] ; p++) if (*p < *(p+1))

swab ( p , (p+1) ); //____________|

mas_print(A , &A[n] , col); //печать отсортированного массива

}

Функция swab() переставляет местами переменные, адресуемые параметрами p1 и p2.

Функция mas_print() выводит массив на экран дисплея, первые два параметра - начало и конец вывода заданы указателями, третий параметр задает количество элементов в строке при выводе.

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

В цикле for проходит формирование массива с помощью датчика случайных чисел. При входе в цикл указатель p устанавливается на начало массива (p=A). Пока указатель p не равен адресу последнего элемента (p<&A[n]) цикл продолжается.

Сортировка массива ведется методом "пузырька" по следующему алгоритму:

1.Сравниваются два соседних элемента, и если текущий элемент меньше последующего, элементы меняют местами. Текущий элемент меняется в диапазоне от A[0] до A[n-1].

Эти действия называются одним проходом сортировки, в результате которой наименьший элемент массива оказывается в его конце (внутренний цикл for).

2.Чтобы "поставить" на свои места все элементы массива, необходимо выполнить n проходов сортировки, то есть повторить алгоритм столько раз, сколько элементов в массиве (внешний цикл for).

Обратите внимание на то, как передаются параметры в функции:

1. Прототип

функции mas_print ():

void mas_print (int

*p_beg, int *p_end, int k)

Вызов функции : mas_print(A , &A[n] , col)

Первый параметр передается c использованием указателя,

напомним, что имя

массива - это константный указатель на элемент

A[0], при вызове происходит

присваивание p_beg=A (в указатель p_beg записывается значение указателя A) Второй параметр - это адрес последнего элемента массива. При вызове происходит присваивание p_end=&A[n] (адрес последнего элемента &A[n] записывается в указатель p_end)Третий параметр передается по значению, переменная col содержит число, которое используется для определения числа колонок при печати массива. При вызове функции происходит присваивание k=col.

2. Прототип функции swab(): void swab (int *p1 , int *p2)

Вызов функции: swab ( p , (p+1)

Оба параметра передаются с использованием указателей, первый параметр указывает на "текущий" элемент, а второй на следующий элемент.

8.5.3. Передача параметров по ссылке

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

Рассмотрим уже известную функцию swab, которая переставляет местами значения двух переменных, определенных во внешней функции main(). Имея транслятор языка С, эту задачу можно решить только с применением указателей, в С++ можно использовать еще и ссылки.

// параметры функции заданы в виде ссылок void swap(int& a, int& b)

{

int temp = a; a = b;

b = temp;

}

void main()

{ int i = 1, j = 2;

. . .

swap(i,j); // после вызова: i=2 j=1

}

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

что ссылка может рассматриваться как синоним переменной, и обращение по ссылке синтаксически ничем не отличается от обращения к обычной переменной (не требуется использование операции разименования). При вызове функции, неявно происходят операции присваивания &a=i и &b = j, то есть создаются ссылки на переменные i и j, с которыми в теле функции можно работать также как с переменными i и j.

8.5.4. Передача массива в функцию

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

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

Если аргументом функции является массив, то её формальный параметр можно объявить тремя способами:

Передача массива через указатель: int func(char* s)

{. . .

}

Передача массива определенного размера: int func (char s[100])

{. . .

}

Передача массива неопределенного размера: int func (char s[])

{ . . .

}

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

Пример: Вычисление суммы массива.

В примере первый параметр определяет сам массив, а второй его размерность.

int summa (int array[ ], int n)

{int res=0;

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

res+ = array[i]; return res;

}

void main()

{// определение массива в 100 элементов int mas[100];

//инициализация массива for (int i = 0; i < 100; i++) mas[i] = 2*i + 1;

//создание переменной s и вычисление суммы

int s = summa (mas, 100);

}

При вызыове функции summa (mas, 100) происходит передача лишь адреса массива mas. Этот процесс можно представить так - массив mas[] получает новое имя array[ ] .

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

Пример: Вычислить длину строки (массива типа char). #include <stdio.h>

int length(char* s)

{int i;

for(i=0; *s!='\0' ; s++,i++)

return(i);

}

void main()

{char *str1="Какая длина у этой строки?"; printf("длина=%d\n",length(str1));

}

Если формальный параметр в определении функции задан в виде указателя, как в функции int length(char* s) , то фактическим параметром при вызове функции может быть:

имя массива, как в примере - length(str1);

адрес какого либо элемента, например пятого элемента строки length(&str1[4]), в этом случае началом строки будет символ "я";

или же указатель, инициированный ранее, например: char *ptr;

ptr=&str[3];

length (ptr);

Пример: Копирование строки s1 в символьный массив s2. void copy(char s1[], char s2[])

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

{int i=0;

 

do // бесконечный цикл

 

{if ((s1[i])=='\0') break;

// выход из цикла

s2[i]=s1[i];

 

i++;

 

}

 

while(true);

 

}

 

void main()

 

{char str1[]="Long string?"; char str2[80];

printf("%s\n",str1); copy(str2 , str1); printf("%s\n",str1);

}

Если формальный параметр в определении функции задан в виде массива, как в функции - void copy(char s1[], char s2[]) , то фактическим параметром при вызове функции может быть только имя массива

copy(str2 , str1).

Пример: Ввести с клавиатуры три массива, вычислить их суммы, отобразить на экране результаты. #include <stdio.h>

// прототипы функций

double summa (double A[] , int n ) void mas_input (double A[] , int n ) void main()

{// определение трех массивов R,M и K double R[10],M[15],K[5],sum; printf("Введите массив R\n"); mas_input (R,10);

printf("Введите массив M\n"); mas_input (M,15); printf("Введите массив K\n"); mas_input (K,5);

printf("Сумма массива R[] =%5.2f\n", summa(R,10)); printf("Сумма массива M[]=%5.2f\n",summa(M,15)); printf("Сумма массива K[] =%5.2f\n", summa(K,5)); } // конец функции main()

// область определения функций : //вычисление суммы произвольного массива double summa (double A[] , int n )

{double s; int i;

for ( i=0,s=0 ; i<n ; i++ ) s=s+A[i];

return s;

}

//ввод с клавиатуры произвольного массива void mas_input (double A[] , int n )

{int i;

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

scanf("%le",A[i]);

}

Впримере определены две функции - mas_input() и summa() для ввода c клавиатуры и подсчета суммы массива произвольной длины.

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

Ввызывающей функции main() определены три массива разной длины - R[10],M[15],K[5], которые задаются с клавиатуры, для этого три раза вызывается функция mas_input().

Так как результат передается в точку вызова функции, мы можем использовать вызов функции summa() в качестве параметра printf(). Приоритет оператора ()"вызов функции", наивысший, поэтому сначала выполняется вызов "вложенной функции" summa(), а затем действия по обеспечению печати.

Как говорилось ранее, в языке С между указателями и массивами существует тесная связь, следующий пример демонстрирует их взаимозаменяемость.

Пример: Взаимозаменяемость указателя и массива. #include <stdio.h>

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

//формальный параметр функции - массив void print1(char s[])

{ printf(s);

}

//формальный параметр функции - указатель void print2(char *s)

{ printf(s);

}

int main()

{// определение строки как массива char str1[]="String -massiv \n";

//определение строки через указатель

char *p_str =" String -pointer \n";

/*взаимозаменяемость массива и указателя при передаче параметров*/

print1(p_str);

// параметр - указатель

print2(str1);

// параметр - массив

/*С индексами обе строки работают одинаково*/ for (int i=0 ; str1[i] ; i++) putchar(str1[i]);

for (int j=0 ; p_str[j] ; j++) putchar(p_str[j]); //Работа с указателями возможна только с p_str for (p_str ; *p_str ; p_str++) putchar (*p_str); return 0;

}

Несмотря на то, что формальный парамерт функции print2 указатель, при вызове функции можно передавать имя массива.

Несмотря на то, что формальный парамерт функции print1 – массив, при вызове функции можно передавать указатель.

Работа с индексами возможна для обеих строк :

str1 объявлен как массив типа char, а индексация - естественная работа с массивами;

p_str - указатель на тип char, но тоже

допускает индексирование.

По сути дела в обоих случаях при индексировании происходит одно и то же - вычисляется адрес очередного элемента строки, к адресу, записанному в указателе (str1 или p_str) прибавляется значение индекса.

Механизм работы с указателями возможен только для строки p_str, так как это полноценный указатель-переменная, значение которой можно менять, что и делает инструкция p_str++ в заголовке цикла for. Имя массива str1- это константный указатель, который всегда указывает на начало массива, поэтому никакая модификация этого указателя недопустима, а без изменения значения адреса в указателе невозможно обращаться к разным элементам массива.

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]