
- •6.1. Общая форма определения функции
- •6.2. Создание функции
- •6.3. Использование аргументов
- •6.4. Прототипы функций
- •6.5. Использование предложения return
- •6.6. Использование функций в выражениях
- •6.7. Основы техники указателей
- •6.7.1. Объявление указателей
- •6.7.2. Операторы указателей
- •6.8. Передача в функции указателей
- •6.9. Передача в функции массивов
- •6.9.1. Указатели и массивы
- •6.9.2. Способы передачи в функции массивов
- •6.10. Передача в функцию строк
- •6.10.1.Строковые константы
- •6.10.2. Передача строк
- •6.11. Возврат указателей
6.8. Передача в функции указателей
Для того, чтобы передать указатель в качестве аргумента, вы должны объявить параметр типа указателя. Вот пример:
// Передача функции указателя.
#include <iostream>
using namespace std;
//f() принимает указатель int * в качестве параметра.
void f (int *j); // f() объявляет параметр-указатель
int main () {
int i;
int *p;
p = &i; // p теперь указывает на i
f(p); // передача указателя
cout << i; // i теперь равно 100
return 0 ;
}
// f() получает указатель на int.
void f (int * j )
{
*j = 100; /* переменной, на которую указывает j,
теперь присвоено значение 100 */
}
Рассмотрим внимательно эту программу. Мы видим, что функция f() принимает один параметр: указатель на int. Внутри main( ) указателю р присваивается значение адреса переменной i. Далее вызывается f() с р в качестве аргумента. Когда параметр-указатель j получает значение р, он так же указывает на i внутри main( ). Таким образом, предложение
*j = 100;
присваивает значение 100 переменной i. В общем случае f( ) запишет значение 100 по любому адресу, который будет ей передан при вызове.
В предыдущем примере фактически не было необходимости использовать переменную-указатель р. Достаточно при вызове f() указать в качестве аргумента i, предварив это имя значком &. В результате в функцию f() будет передан адрес i. Модифицированная программа имеет такой вид:
// Передача функции указателя.
#include <iostream>
using namespace std;
void f(int *j ) ;
int main() {
int i ;
f(&i); //Нет необходимости в p. В f() непосредственно
// передается адрес i.
cout << i ;
return 0;
}
void f(int *j ) {
*j = 100; // переменной, на которую указывает j,
// теперь присвоено значение 100
}
Существенно, чтобы вы усвоили важное обстоятельство, связанное с передачей в функции указателей: когда вы выполняете внутри функции операцию, использующую указатель, вы фактически работаете с переменной, на которую этот указатель указывает. Это дает возможность функции изменять значение объекта, на который указывает аргумент-указатель.
6.9. Передача в функции массивов
6.9.1. Указатели и массивы
В С++ имеется тесная взаимосвязь между указателями и массивами. Во многих случаях указатель и массив являются взаимозаменямыми понятиями. Рассмотрим следующий фрагмент:
char str[80];
char *pl;
p1 = str;
Здесь str представляет собой массив из 80 символов, a p1 - указатель на символы. Однако для нас более интересна третья строка. В ней p1 присваивается адрес первого элемента массива str. (После присваивания p1 будет указывать на str[0].) И вот почему: в С++ использование имени массива без индекса образует указатель на первый элемент массива. Таким образом, операция
p1 = str;
присваивает указателю p1 адрес элемента str[0]. Это очень важный момент: при использовании в выражении неиндексированного имени массива образуется указатель на первый элемент массива.
Поскольку после присваивания p1 указывает на начало str, вы можете посредством p1 получить доступ к элементам массива. Если, например, вы хотите обратиться к пятому элементу str, вы можете использовать обозначения
str[4]
или
*(р1+4)
Оба выражения дадут вам значение пятого элемента. Не забывайте, что индексы массива начинаются с нуля, поэтому при индексации str для доступа к пятому элементу используется индекс 4. Для обращения к пятому элементу посредством указателя p1 к значению p1 также добавляется 4, так как сам p1указывает на первый элемент str.
Круглые скобки, окружающие pl+4 в последнем выражении, необходимы, потому что операция * имеет более высокий относительный приоритет, чем операция +. Если скобки опустить, выражение будет вычислять иначе: сначала будет найдено значение, на которое указывает p1 (т. е. первый элемент массива), а затем к этому значению прибавится 4.
Фактически С++ предоставляет два метода обращения к элементам массива: с помощью арифметики указателей и с помощью индексации массива. Необходимо владеть обоими методами, так как иногда арифметика указателей работает быстрее индексации - особенно, если вы обращаетесь к элементам массива в строго последовательном порядке. Поскольку скорость часто является важнейшим критерием в программировании, обращение к элементам массивов посредством указателей весьма распространено в программах на С++. Кроме того, используя указатели вместо индексации массива, вы иногда можете написать более компактную программу.
Ниже приведен пример, демонстрирующий различия между индексацией массива и арифметикой указателей при обращении к элементам массива. Мы рассмотрим два варианта программы, которая будет выполнять преобразование букв в строке: прописных в строчные, а строчных — в прописные (обращение регистра). В первом варианте используется индексация. Второй вариант делает то же, но с помощью арифметики указателей. Первый вариант программы выглядит так:
// Обращение регистра посредством индексации массива.
#include <iostream>
#include <cctype>
using namespace std;
int main() {
int i;
char str[80] = "This Is A Test";
cout << "Исходная строка: " << str << "\n";
for(i = 0; str [i] ; i ++) {
if(isupper(str[i]))
str[i] = tolower(str[i]);
else if(islower(str[i]))
str[i] = toupper(str[i]);
}
cout << "Преобразованная строка: " << str;
return 0;
}
Вывод программы выглядит так:
Исходная строка: This Is A Test
Преобразованная строка: tHIS iS a tEST
Обратите внимание на то, что программа с целью определения регистра использует библиотечные функции isupper() и islower(). Функция isupper( ) возвращает true, если ее аргумент является прописной буквой; функция islower() возвращает true, если ее аргумент - строчная буква. Внутри цикла for выполняется индексация массива str, и каждая буква анализируется и преобразуется. Цикл повторяется до тех пор, пока не встретится завершающий строку ноль. Поскольку ноль равнозначен false, цикл завершается.
Теперь рассмотрим вариант программы с арифметикой указателе
// Обращение регистра посредством арифметики указателей.
#include <iostream>
#include <cctype>
using namespace std;
int main()
{
char *p;
char str[80] = "This Is A Test";
cout << "Исходная строка: " << str << "\n";
p = str; // присвоим p адрес начала массива
while(*p) {
if (isupper ( *p) )
*p = tolower ( *p) ;
else if ( islower ( *p) )
*p = toupper(*p) ;
p++;
}
cout << " Преобразованная строка: " << str;
return 0;
}
В этом варианте программы р устанавливается на начало str. Затем внутри цикла while буква, на которую указывает р, анализируется и преобразуется, а затем выполняется инкремент р. Цикл завершается, когда р будет указывать на завершающий строку str ноль.