4.6.5 Параметр-массив.

   


Назад  |  Вперёд

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

           int strlen(const char*);

           void f()
           {
             char v[] = "массив";
             strlen(v);
             strlen("Николай");
           }

Это означает, что фактический параметр типа T[] преобразуется к типу T*, и затем передается. Поэтому присваивание элементу формального параметра-массива изменяет этот элемент. Иными словами, массивы отличаются от других типов тем, что они не передаются и не могут передаваться по значению.

В вызываемой функции размер передаваемого массива неизвестен. Это неприятно, но есть несколько способов обойти данную трудность. Прежде всего, все строки оканчиваются нулевым символом, и значит их размер легко вычислить. Можно передавать еще один параметр, задающий размер массива. Другой способ: определить структуру, содержащую указатель на массив и размер массива, и передавать ее как параметр (см. также $$1.2.5). Например:

           void compute1(int* vec_ptr, int vec_size);  // 1-ый способ

           struct vec {               // 2-ой способ
             int* ptr;
             int size;
           };

           void compute2(vec v);

Сложнее с многомерными массивами, но часто вместо них можно использовать массив указателей, сведя эти случаи к одномерным массивам. Например:

           char* day[] = {
              "mon", "tue", "wed", "thu", "fri", "sat", "sun"
           };

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

           void print_m34(int m[3][4])
           {
              for (int i = 0; i<3; i++) {
                  for (int j = 0; j<4; J++)
                      cout << ' ' << m[i][j];
                  cout << '\n';
             }
           }

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

Первая размерность для вычисления адреса элемента неважна ($$R.8.2.4), поэтому ее можно передавать как параметр:

           void print_mi4(int m[][4], int dim1)
           {
              for ( int i = 0; i<dim1; i++) {
                  for ( int j = 0; j<4; j++)
                      cout << ' ' << m[i][j];
                  cout << '\n';
              }
           }

Самый сложный случай - когда надо передавать обе размерности. Здесь "очевидное" решение просто непригодно:

           void print_mij(int m[][], int dim1, int dim2)   // ошибка
           {
             for ( int i = 0; i<dim1; i++) {
                 for ( int j = 0; j<dim2; j++)
                     cout << ' ' << m[i][j];
                 cout << '\n';
             }
           }

Во-первых, описание параметра m[][] недопустимо, поскольку для вычисления адреса элемента многомерного массива нужно знать вторую размерность. Во-вторых, выражение m[i][j] вычисляется как *(*(m+i)+j), а это, по всей видимости, не то, что имел в виду программист. Приведем правильное решение:

           void print_mij(int** m, int dim1, int dim2)
           {
              for (int i = 0; i< dim1; i++) {
                  for (int j = 0; j<dim2; j++)
                    cout << ' ' << ((int*)m)[i*dim2+j];  // запутано
                  cout << '\n';
             }
           }

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

           int* v = (int*)m;
           // ...
           v[i*dim2+j]

Лучше такие достаточно запутанные места в программе упрятывать. Можно определить тип многомерного массива с соответствующей операцией индексирования. Тогда пользователь может и не знать, как размещаются данные в массиве (см. упражнение 18 в $$7.13).


Назад  |  Вперёд


OtDiatlovaOU
К началу книги

Оформление и дизайн книги OtDiatlovaOU.
Вся книга, архив.