Ответ: из Стаурструпа(немного теории . . .) (+)
(«Телесистемы»: Конференция «Микроконтроллеры и их применение»)

миниатюрный аудио-видеорекордер mAVR

Отправлено volkanaft 07 сентября 2005 г. 19:38
В ответ на: так известно и не интересно отправлено ы 07 сентября 2005 г. 19:25

...
Но в том и загвоэдка, что обойти это нельзя: не существует способа так описать функцию, чтобы при ее вызове массив v копировался ($$4.6.3).
>>>$$4.6.3
При вызове функции выделяется память для ее формальных параметров,
и каждый формальный параметр инициализируется значением
соответствующего фактического параметра. Семантика передачи
параметров тождественна семантике инициализации. В частности, сверяются
типы формального и соответствующего ему фактического параметра, и
выполняются все стандартные и пользовательские преобразования типа.
Существуют специальные правила передачи массивов ($$4.6.5).
$$4.6.5
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 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 for ( int j = 0; 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 cout << ' ' << ((int*)m)[i*dim2+j]; // запутано
cout << '\n';
}
}

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

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

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


Составить ответ  |||  Конференция  |||  Архив

Ответы



Перейти к списку ответов  |||  Конференция  |||  Архив  |||  Главная страница  |||  Содержание  |||  Без кадра

E-mail: info@telesys.ru