Бьерн Страуструп - Язык программирования С++. Главы 2-4
Страница 62. Параметр-массив


 

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).

 
« Предыдущая статья   Следующая статья »