Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lek6-7.doc
Скачиваний:
5
Добавлен:
15.11.2018
Размер:
98.3 Кб
Скачать

Массивы и параметры

В C++ возможно лишь поэлементное копирование массивов. Этим объясняется то обстоятельство, что в списке объявлений параметров не объявляются параметры-массивы. Транслятор спокойно реагирует на объявление одномерного массива в заголовке функции, проверяет корректность его объявления (размеры массива должны быть представлены константными выражениями), однако сразу же игнорирует эту информацию. Объявление одномерного массива-параметра преобразуется к объявлению указателя. Подтверждением этому служит тот факт, что "массив"- параметр невозможно проинициализировать списком значений, что совершенно нормально для обычных массивов:

void ff(int keyArr[ ] = {0,1,2,3,4,5,6,7,8,9});// Ошибка объявления.

void ff(int keyArr[10] = {0,1,2,3,4,5,6,7,8,9});// Ошибка объявления.

Оба варианта прототипа функции будут отвергнуты. При этом транслятор утверждает, что указателю (и это несмотря на явное указание размеров массива!) можно присваивать значение адреса, либо NULL.

int keyArr[100]; // Глобальный массив.

int xArr[5]; // Еще один глобальный массив.

int XXX; // Простая переменная.

void ff(int keyArr[ 1] = keyArr, //Объявление одноименного параметра.

int pArr1 [10] = xArr,

int pArr2 [ ] = &XXX, // Адрес глобальной переменной.

int pArr3 [ ] = &xArr[10], //Адрес несуществующего элемента.

int pArr4 [50] = NULL);

/* Допустимые способы инициализации массивов в прототипе функции

свидетельствуют о том, что здесь мы имеем дело с указателями. */

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

#include <iostream.h>

void fun(int *, int[], int qwe[10] = NULL);

/* Все три объявления параметров на самом деле являются объявлениями указателей. */

void main()

{

int Arr[10] = {0,1,2,3,4,5,6,7,8,9};

int *pArr = Arr;

/* В функции main определены массив и указатель.*/

cout << Arr << " " << &Arr << " " << &Arr[0] << endl;

cout << pArr << " " << &pArr << " " << &pArr[0] << endl;

/* Разница между массивом и указателем очевидна: значение выражения,

представленного именем массива, собственный адрес массива и адрес

первого элемента массива совпадают. */

fun(Arr, Arr, Arr);

}

void fun(int* pArr1, int pArr2[], int pArr3[100])

{

cout << sizeof(pArr1) << endl;

cout << sizeof(pArr2) << endl;

cout << sizeof(pArr3) << endl;

cout << pArr1 << " " << &pArr1 << " " << &pArr1[0] << endl;

cout << pArr2 << " " << &pArr2 << " " << &pArr2[0] << endl;

cout << pArr3 << " " << &pArr3 << " " << &pArr3[0] << endl;

/* Все параметры проявляют свойства указателей. */

}

Так что размеры массива в объявлении параметра, подобно имени параметра в прототипе, являются лишь украшением, которое предназначается для напоминания программисту о назначении параметра.

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

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

#include <iostream.h>

void fun(int * = NULL, int = 0);

void main()

{

int Arr[10] = {0,1,2,3,4,5,6,7,8,9};

fun(Arr, 10);

fun(Arr, sizeof(Arr)/sizeof(Arr[0]));

}

void fun(int* pArr /* int pArr[] */ /* int pArr[150] */, int key)

{

for ( key--; key >= 0; key--) cout << pArr[key] << endl;

}

Фактическое тождество одномерного массива и указателя при объявлении параметров определяет специфику объявления многомерных массивов-параметров. В C++ многомерный массив - понятие условное. Как известно, массив размерности n является одномерным массивом множества объектов производного типа - массивов размерности n-1. Размерность массива является важной характеристикой производного типа. Отсюда - особенности объявления многомерных массивов как параметров функций.

В следующем примере определена функция fun с трехмерным параметром размерности 5*5*25. Транслятор спокойно реагирует на различные варианты прототипов функции fun в начале программы. Если последовательно комментировать варианты объявлений функции, ошибка будет зафиксирована лишь тогда, когда будут закомментированы все объявления, у которых характеристика второй и третьей размерности совпадает с аналогичной характеристикой многомерного параметра-массива в определении функции.

#include <iostream.h>

#define DIM1 3

#define DIM2 5

// void fun(int rrr[][][]);

/* Такой прототип неверен! Квадратные скобки в объявлении параметра,

начиная со второй, обязательно должны содержать константные выражения,

значения которых должны соответствовать значениям в квадратных скобках

(начиная со второй!) в объявлении параметра в определении функции. Эти

значения в контексте объявления параметров являются элементами

спецификации ТИПА параметра, а не характеристиками его РАЗМЕРОВ. Типы

составляющих одномерные массивы элементов в прототипе и заголовке

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

//void fun(int rrr[5][DIM1][DIM2]);

void fun(int rrr[][3][5]);

void fun(int rrr[15][DIM1][5]);

void fun(int *rrr[3][DIM2]);

/* Во всех этих случаях параметр rrr является указателем на двумерный

массив из 3*5 элементов типа int. "Массив из трех по пять элементов типа

int" - такова спецификация типа объекта. */

/* Следующие два прототипа, несмотря на одно и то же имя функции,

объявляют еще пока неопределенные фунции. Одноименные функции с

различными списками параметров называются перегруженными функциями. */

void fun(int *rrr[25][250]);

void fun(int rrr[50][100][DIM1]);

void main()

{

int Arr[2][DIM1][DIM2] = {

{

{1 ,2 ,3 ,4 ,5 },

{10,20,30,40,50},

{11,12,13,14,15},

},

{

{1,},

{2,},

{3,},

}

};

fun(Arr); // Вызов fun. Значение параметра - адрес начала массива.

}

void fun(int pArr[75][DIM1][DIM2])

{

cout << sizeof(pArr) << endl;

cout << pArr << " " << &pArr << " " << &pArr[0][0] << endl;

/* Параметр проявляет свойства указателей. */

cout << sizeof(*pArr) << endl;

cout << *pArr << " " << &*pArr << " " << &*pArr[0][0] << endl;

/* Если применить к указателю операцию разыменования, можно убедиться в том,

что параметр указывает на массив. При этом о топологии многомерного массива

можно судить исключительно по косвенной информации (в данном случае - по

значениям константных выражений DIM1 и DIM2) или по значениям дополнительных

параметров. */

}

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

Резюме

Аргументом функции может быть целый массив, и тогда этот аргумент передается осо­бым образом: не по ссылке и не по значению. Такие аргументы называются аргумента­ми типа массива. Когда аргумент типа массива подставляется в параметр типа массива, функция получает единственное значение — адрес первого элемента массива в памяти (то есть элемента массива с индексом 0). Размер массива не передается функции авто­матически, поэтому в ее объявлении обычно определяют дополнительный параметр типа int, в котором явно задается размер массива (как в приведенном ниже примере).

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

Синтаксис

возвращаемый_тип имя_функции (... . базовый_тип_имя_массива[]. ...);

Пример

void sum_array(double& sum, double a[], int size);

Ловушка: несогласованное использование квалификатора const

Квалификатор параметра const действует по принципу «все или ничего». Если он используется для параметра типа массива конкретного типа в одной из функций программы, то должен применяться и во всех остальных функциях программы, имеющих параметр того же типа и не изменяющих его значение. Это связано с ча­сто встречающимися в программах на C++ вызовами одних функций другими. Рассмотрим приведенное ниже определение функции show_diffегеnсе и объявле­ние используемой в нем функции compute_average:

double compute_average(int a[], int number_used):

// Возвращает среднее арифметическое первых number_used

// элементов массива а. Массив а не изменяется.

void show_difference(const int a[], int number_used)

{

double average = compute_average(a, number_used);

cout « "Average of the " « number_used « " numbers = " « average « end! « "The numbers are:\n";

for (int index = 0; index < number_used; index++)

cout « a[index] « " differs from average by " «

(a[index]-average) « endl; }

Большинство компиляторов выведет для данного кода сообщение об ошибке. Функ­ция compute_average не изменяет параметр а. Но когда компилятор анализирует оп­ределение функции show_diff егеnсе, он обнаруживает, что функция compute_average может изменять значение своего параметра типа массива. Это происходит пото­му, что во время обработки определения функции show_difference компилятору ничего не известно о функции compute_average, кроме ее объявления. В этом объяв­лении отсутствует квалификатор const, из чего следует, что для функции сотри-teaverage изменение параметра а не запрещено. Чтобы определение функции showdifference было откомпилировано без сообщений об ошибках, объявление функции compute_average должно быть таким:

double compute_average(const int a[], int number_used);

Функции, возвращающие массивы

Функция не может вернуть массив тем же способом, каким она возвращает обыч­ные значения типа int или double. Однако возможность получения массива в ка­честве результирующего значения функции все же существует: нужно получить от функции указатель на массив. Но вы пока не можете написать функцию, возвращаю­щую указатель на массив, поскольку мы еще не рассматривали указатели. К этому вопросу мы вернемся в лекции 12.

ПРИМЕР

В качестве примера рассмотрим следующую функцию, принимающую массив, та­кой как page, и выводящую на экран его содержимое:

void display_page(const char p[][100], int size_dimension_1)

{

for (int index1 = 0;

index1 < int size_dimension_1; index1++) {

// Вывод одной строки:

for (int index2 = 0; index2 < 100; index2++)

cout « p[index1][index2]; cout « endl; } }

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