LINUX.ORG.RU

[C/C++] Указатель и размерность массива


0

0

С определенного момента программирования на C/C++ меня мучит один вопрос, 
да все стеснялся или забывал спросить.

Например вот кусок программы:

#define N 12

int func(int *array)
{
	int i, j;
	i = j = N/2;
	return *(array + i*N + j); //вернуть центральный елемент матрицы
}

int main()
{
	int array[N][N];
	// ... как-то там заполняем массив

	int center = func(&array[0][0]);
}

Для получения доступа к массиву array функции func(int *) передается
указатель на целое (в этом случае на первый елемент массива), но 
в самой функции уже нельзя писать "красивое" обращение к элементу двухмерного 
массива (например array[i][j]) и приходится работать с адресами ( return *(array + i*N + j) ).

Вопрос:

Можно ли как-то передать или позже преобразовать указатель, чтобы процедура
понимала что это двухмерный (многомерный) массив с определенной размерностью,
и можно было бы обратится к его элементу как array[i][j], а не *(array + i*N + j).

Спасибо!

P.S. Если вопрос тривиальный и ответ рассказывают на первом уроке по C/C++, прошу сильно не пинать. :)

Можно обращаться к двумерному массиву, если обьявлять и передавать его как int ** array.

Касаемо размеров массива. Скорее всего никак. Нужно знать как работает аллокатор. Нужно знать информацию о блоке выделенной памяти. И тогда, может быть, получится вычислить количество элементов в блоке памяти по типу count = block_size / sizeof( type );

chak
()
Ответ на: комментарий от chak

> Можно обращаться к двумерному массиву, если обьявлять и передавать его как int ** array

А как, в таком случае, узнать где заканчивается одна строка массива и начинается другая, ведь логика обращения array[i][j], основывается на извесности размерности массива, если я не прав прошу обьяснить с момента "Можно обращаться к двумерному массиву, если обьявлять и передавать его как int ** array" по-подробнее.

fifajan
() автор топика
Ответ на: комментарий от chak

> Можно обращаться к двумерному массиву, если обьявлять и передавать его как int ** array.

Лучше бы ты этого не говорил.

Для OP: в общем случае - никак (в Си). В Си++ можно создать класс(ы) для многомерного массива.

Для частного случая многомерного масива с заданной размерностью:

int func(int a[][N], int dim1) { }

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Для частного случая многомерного масива с заданной размерностью:

> int func(int a[][N], int dim1) { }

int a[][N] это указатель?

а передават туда как?:

int array[N][N]; int J_dim;

func(<* как? *>, J_dim);

fifajan
() автор топика
Ответ на: комментарий от fifajan

> int a[][N] это указатель?

Это массив, первая размерность которого неизвестна (поэтому передается как параметр).

void func(int a[][M], int dim);

int array[N][M];

func(array, N);

tailgunner ★★★★★
()

я когда-то делал так (только удалять массив нужно аккуратно):


#include <iostream>

using std::cout;
using std::endl;

void fill_array(int **array, int rows, int cols)
{
int fill = 1;

for(int i = 0; i < rows; i++)
for(int j = 0; j < cols; j++)
array[i][j] = fill++;
}

void print_array(int **array, int rows, int cols)
{
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
cout << array[i][j] << "\t";

cout << endl;
}
}

int main()
{
int **A;
int rows = 3, cols = 4;

A = new int *[rows];

for(int i = 0; i < rows; i++)
A[i] = new int[cols];

fill_array(A, rows, cols);

print_array(A, rows, cols);

return 0;
}

anonymous
()
Ответ на: комментарий от tailgunner

> Это массив, первая размерность которого неизвестна (поэтому передается как параметр).

Так оно передается "копированием" памяти?

fifajan
() автор топика
Ответ на: комментарий от anonymous

Кто мешает сменить порядок?

int sum(int N, int M, int arr[N][M]) { int i,j ; int r = 0; for (i=0; i<N; i++) for(j=0; j<M; j++) r += arr[i][j]; return r; } int main() { int arr[3][3]; int i,j ; int fill = 0; for (i=0; i<3; i++) for(j=0; j<3; j++) arr[i][j] = ++fill; printf ("sum is %d\n", sum(3,3,arr)); }

io ★★
()
Ответ на: комментарий от io

Сорри забыл про форматирование.

int sum(int N, int M, int arr[N][M])
{
int i,j ;
int r = 0;
for (i=0; i<N; i++)
for(j=0; j<M; j++)
r += arr[i][j];
return r;
}

int main()
{
int arr[3][3];
int i,j ;
int fill = 0;
for (i=0; i<3; i++)
for(j=0; j<3; j++)
arr[i][j] = ++fill;
printf ("sum is %d\n", sum(3,3,arr));
}

io ★★
()
Ответ на: комментарий от tailgunner

ОК, тогда это то о чем я спрашивал, спасибо!

fifajan
() автор топика
Ответ на: комментарий от tailgunner

C99:

void fvla(int m, int C[m][m]) // OK - VLA with prototype scope.
{
typedef int VLA[m][m] // OK - block scope typedef VLA.

int bimbom[m][m] - Тоже вполне нормально внутри.

io ★★
()
Ответ на: комментарий от io

Странно, это не "фокусы компилятора"? Тогда появляется вопрос, а как, в таком случае, осуществить копирование передваваемой переменной (массива)?

fifajan
() автор топика
Ответ на: комментарий от tailgunner

Странно, я всегда думал, что запись предложенная io, порождает копирование массива.

Спасибо, теперь буду пользоватся не опасаясь.

fifajan
() автор топика
Ответ на: комментарий от tailgunner

Елки! не так прочитал то, что в скобках, ну тогда твой совет заюзаю :)

И теперь последний вопрос, можно скопирывать указатель с
размерностью, например, вот так:

void func(int a[][M], int dim)
{
    <* копируем указатель a в указатель b *>;
    int i = b[2][3];
    // было бы совсем чудно, если это можно осуществить.
}

int array[N][M]; 

func(array, N);

fifajan
() автор топика
Ответ на: комментарий от fifajan

> void func(int a[][M], int dim)
> {
>     <* копируем указатель a в указатель b *>;
>     int i = b[2][3];

Если b имеет тип int *, выражение b[2][3] - ошибка времени компиляции.

tailgunner ★★★★★
()

Надёжное решение:

struct Matrix {
  char** data;
  unsigned rows;
  unsigned cols;
};

void matrix_init(struct Matrix* m);
void matrix_fini(struct Matrix* m);

anonymous
()
Ответ на: комментарий от fifajan

> А как, в таком случае, узнать где заканчивается одна строка массива и начинается другая, ведь логика обращения array[i][j], основывается на извесности размерности массива, если я не прав прошу обьяснить с момента "Можно обращаться к двумерному массиву, если обьявлять и передавать его как int ** array" по-подробнее.

А ничего не надо знать - int ** - это указатель на указатель... Выскажусь грубо - "массив указателей на массивы"...

Теперь как этим пользоваться...

int ** array;
int cols = 3;
int rows = 3;

Создавать так:

array = new int * [rows];
for( int i = 0; i < rows; ++i ) {
   array[i] = new int[cols];
}

Удалять в обратном порядке:

for( int i = 0; i < rows; ++i ) {
   delete[] array[i];
}
delete[] array;

chak
()
Ответ на: комментарий от chak

Это нечестный двумерный массив. Мы имеем дело с rows+1 одномерными массивами. Но работаем как с двумерными =)

chak
()
Ответ на: комментарий от fifajan

> Странно, это не "фокусы компилятора"? Тогда появляется вопрос, а как, в таком случае, осуществить копирование передваваемой переменной (массива)?

Почитай про язык C что-нибудь, вопросы такие отпадут

anonymous
()

#define array(i),j) *(a +(j) + (i)*n) И использовать обращения вида array(i,j) ?

anonymous
()
Ответ на: комментарий от chak

> Создавать так:
>
> array = new int * [rows];
> for( int i = 0; i < rows; ++i ) {
>    array[i] = new int[cols];
> }

То, что создаётся в этом случае, представляет из себя Jagged Array. Вещь, неудобная как из соображения оптимизации кеширования памяти, так и её выделения.

Если нужно, чтобы обращения были прозрачными
Оптимально определять локально на стеке матрицу с нужными
размерностями и массив?костыль, который инициализировать
указателями на начало каждой строки. Локально мы сможем
использовать эту матрицу, потому что мы её так объявили, а
в вызываемой функции -- потому что она будет работать через
указатель на указатели :

void test (int ** matrix) {
// use matrix [i][j] here
}

int main () {
     int matrix [M][N];
     int * matrix_rows [M];
     for (int i = 0; i < M; i++) {
          matrix_rows [i] = matrix [i];
     }
     // use matrix [i][j] here
     test (matrix_rows);
     return 0;
}

Либо, как уже указывалось, завести линейный массив размерностью
M * N, передавать размерности и обращаться по matrix [N * i + j]

Либо, конечно, boost.

Nihilist
()
Ответ на: комментарий от fifajan

> Тогда появляется вопрос, а как, в таком случае, осуществить копирование передваваемой переменной (массива)?

Попробовать завернуть массив в struct и передать не по указателю.

Nihilist
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.