Механизм передачи параметров — по ссылке — подразумевает возможность изменения подпрограммой данных вызывающего фрагмента программы. Для этого в качестве параметра вызывающий фрагмент должен использовать переменную, адрес которой будет передан в подпрограмму через стек. Далее подпрограмма обеспечивает доступ к переданному адресу по имени параметра (возможность прямого доступа к передаваемым данным). Соответственно, изменения, производимые с параметром в подпрограмме, влияют на ту переменную, которая указана в качестве параметра.
Механизм передачи параметров по ссылке
Вызов по ссылке хорош в смысле производительности, потому что он исключает накладные расходы на копирование больших объемов данных; в то же время он может ослабить защищенность, потому что вызываемая функция может испортить передаваемые в нее данные.
Вызов по ссылке можно осуществить двумя способами: с помощью ссылочных параметров и с помощью указателей. Ссылочный параметр — это ПСЕВДОНИМ соответствующего аргумента. Чтобы показать, что параметр функции передан по ссылке, после типа параметра в прототипе функции ставится символ амперсанда "&"; такое же обозначение используется в списке типов параметров в заголовке функции. В вызове такой функции реально в функцию передается не сама переменная, а ее адрес, полученный операцией адресации "&". Тогда упоминание в теле вызываемой функции переменной по имени ее параметра в действительности является обращением к исходной переменной в вызывающей функции, и эта исходная переменная может быть изменена.
Альтернативной формой передачи параметра по ссылке является использование указателей. Тогда адрес переменной передается в функцию не операцией адресации "&", а операцией косвенной адресации "*". В списке параметров подобной функции перед именем переменной указывается символ "*", свидетельствуя о том, что передается не сама переменная, а указатель на нее.
// Типичная Ошибка!!
void GetArray(int *a) { a=new int[10]; cout<<"Enter array"<<endl; for(int i=0; i<10; i++) cin>>a[i]; } | void main() { int* a; //передается указатель, а он – не определен) GetArray(a); PutArray(a); } |
Выделение памяти под параметр а, приводит к потере ссылки на область памяти, находящейся в параметре при его передаче. Для того, чтобы не потерять ссылку на область памяти, отведенной в под массив следует процедуру исправить следующим образом:
void GetArray(int* & a) // ссылка на указатель!!
{
a=new int[10];
cout<<"Enter array"<<endl;
for(int i=0; i<10; i++)
cin>>a[i];
}
При передаче параметра по ссылке аргументом может быть ТОЛЬКО ПЕРЕМЕННАЯ (простая или структурированная). В этом случае в подпрограмму передается не значение переменной, а ее адрес. Для того, чтобы по нему могло быть занесено новое значение передаваемой в качестве параметра переменной, следует воспользоваться операцией, например, получения адреса — “&”. При этом изменяется работа с данным аргументом в самой подпрограмме: необходимо параметр описать как указатель и использовать, при доступе к нему, операции работы с указателями, что, естественно, немного усложняет текст.
void swap (int *a, int *b) { int t; t=*a; *a=*b; *b=t; } | void main () { ... swap (&x,&y); ... } | swap объявлена как функция с двумя аргументами типа указателей на int. При вызове функции swar (&x,&y) адрес x запоминается в a и адрес y запоминается в b. Теперь для одной и той же ячейки существует два имени - синонимы. Ссылки *a и *b в функции swap действуют точно так же, как x и y в main. Присваивание внутри функции swap изменяет содержимое x и y. Компилятор проведет проверку типов аргументов при вызове swap в соответствии с forward-объявлении swap. Типы фактических аргументов соответствуют списку типов аргументов и списку формальных параметров. |
Задача: Требуется в заданном массиве из целых чисел поменять местами 2-ой и 6-ой элементы. Требование: в главной программе должны быть только вызовы подпрограмм.
Решение
Создадим следующие процедуры:
o void GetMem(int * &x, int n); — для того, чтобы отвести память под массив х из n элементов;
o void DoRandom(int *a, int n); — для того, чтобы задать значения элементов массива а из n элементов с помощью датчика случайных чисел;
o void PrintX(int *x, int n); — для того, чтобы напечатать массив х из n элементов;
o void Change(int*a, int*b); — для того, чтобы поменять местами значения элементов, расположенные по адресам a и b.;
/* функция меняет местами значения элементов по адресам а и в */
void Change(int*a, int*b)
{
int z = *a;
*a = *b;
*b = z;
}
/* функция выделяет память под массив */
void GetMem(int * &x, int n)
{
x = new int [n];
}
/* функция создает массив ДСЧ */
void DoRandom(int *a, int n)
{
for (int i=0; i<n; i++)
a[i] = random(100);
}
/* функция печатает массив */
void PrintX(int *x, int n)
{
cout<< " Array: "<<endl;
for (int i=0; i<n; i++)
cout<<x[i]<<" ";
cout<<endl;
}
/* Головная программа */
void main()
{
int* x; // объявить указатель на массив х
GetMem(x,10); // отвести память под массив х
DoRandom(x,10); // задать элементы массива
PrintX(x,10); // напечатать массив
Change(&x[2],&x[6]); // поменять местами значения элементов
PrintX(x,10); // напечатать измененный массив
}