В случае если функция должна менять свои аргументы, можно использовать указатели. Указатели также передаются по значению, и внутри функции создается локальная переменная – указатель. Но так как в нее записан адрес фактического параметра, то через этот адрес значение фактического параметра можно менять.
Например, функция, меняющая значения своих аргументов местами.
void swap(int* x, int* y)
{
int t = *x;
*x = *y;
*y = t;
}
void main()
{
int a = 3, b = 7;
swap (&a, &b);
}
Некоторую особенность имеет использование массивов в качестве аргументов. Эта особенность заключается в том, что массив всегда передаётся в функцию через указатель. Так как имя массива преобразуется к указателю на его первый элемент, то при передаче массива происходит передача этого указателя. По этой причине вызываемая функция не может отличить, относится ли передаваемый ей указатель к началу массива или к одному единственному объекту.
Существует три способа передать массив в качестве параметра функции.
Первый способ – объявление параметра как массива, совпадающего по типу и размеру с массивом, используемым при вызове функции. Например:
const int n = 15;
float avg(int arr[n])
{
float s = 0;
for (int i = 0; i < n; i++)
s += arr[i];
return s/n;
}
void main()
{
int m[n];
for (int i = 0; i < n; i++)
m[i] = i;
cout << avg(m) << '\n';
}
Нужно понимать, что здесь в функцию передаётся не весь массив m целиком, а только указатель на его первый элемент. Поэтому arr становится вторым именем массива m, они ссылаются на одну область памяти. Следовательно, если элементы массива изменяются внутри функции, то при выходе из нее эти изменения будут сохранены во внешней программе.
Второй способ – объявить параметр в виде безразмерного массива, то есть
float avg(int arr[])
В остальном этот способ полностью аналогичен предыдущему.
Третий способ – объявление параметра в виде указателя на тип элементов массива. Именно этот вариант чаще всего применяется при написании программ на C++.
float avg(int *arr)
Так как любой указатель в C++ может быть индексирован операцией [], то обращаться к указателю arr можно как к обычному массиву. То есть здесь, как и в предыдущем случае, остальная часть программы останется без изменений.
Именно так в подавляющем большинстве случаев в функции передаются строковые параметры. Так как строка является массивом символов, то для её передачи используются параметры типа char * (если строка может меняться внутри функции) или const char * (если функция не должна менять строку).
При передаче многомерных массивов без изменения можно использовать только первый способ. Например, передача трехмерного массива размером nxmxl.
float avg(int arr[n][m][l])
Второй способ (безразмерный массив) работает только для первой размерности массива. Все остальные размеры должны быть явно заданы.
float avg(int arr[][m][l])
Третий способ – передача массива через указатель возможен только для динамических массивов, которые были объявлены в виде указатель на указатель, и для которых соответствующим образом была выделена память. Рассмотрим этот пример подробнее.
#include <iostream>
#include <time.h>
using namespace std;
float avg(int ***arr, int n, int m, int l)
{
float s = 0;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
for(int k = 0; k < l; k++)
s += arr[i][j][k];
return s/(n*m*l);
}
void main()
{
setlocale(LC_ALL, "Russian");
srand(time(NULL));
int ***cube; // трехмерный массив
int n, m, l;
cout << "Введите 3 размера массива\n";
cin >> n >> m >> l;
// выделяем память
cube = new int**[n];
for(int i = 0; i < n; i++) {
cube[i] = new int*[m];
for(int j = 0; j < m; j++)
cube[i][j] = new int[l];
}
// заполняем массив
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
for(int k = 0; k < l; k++)
cube[i][j][k] = rand() % 100;
// считаем среднее арифметическое
cout << avg(cube, n, m, l) << '\n';
// освобождаем память из-под массива
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++)
delete[] cube[i][j];
delete[] cube[i];
}
delete[] cube;
system("pause");
}
Здесь проявляется ещё одна особенность работы с массивами в функциях – передача размерностей массива в качестве параметров. Так как в C++ размер массива не хранится вместе с ним, его приходится как-то передавать внутрь функции. Один из способов показан выше – передача размеров через аргументы вместе с самим массивом.
Второй способ – использование глобальных переменных, объявленных в начале программы вне любых функций. Такие переменные доступны как в функции main, где им присваиваются значения, так и в функции по обработке массива, где эти значения считываются.
Третий способ – реализация массива в виде абстрактного типа данных – класса или структуры, когда размер массива хранится вместе с самим массивом внутри одной переменной. Работа с такими типами данных будет рассмотрена позднее.