LINUX.ORG.RU

[C][noob] Передача в функцию матрицы

 ,


0

0

Newbie reporting in. Вопрос по поводу сабжа. Хотелось бы писать что-то вида

void print_matrix(float* m, int x_size, int y_size){
...
    printf("%f", m[i][j]);
...
print_matrix( my_matrix );
В ответ компилятор ругается, мол «can`t convert 'float (*)[N]' to 'float*'». Одним словом нужен способ передать в функцию матрицу любой размерности, по указателю.

ЗЫ: я так понимаю в С нет дмумерных мыссивов (я не о массиве массивов)? Запись вида «float m[N, N]» не работает.

void print_matrix(float* m[], int x_size, int y_size){
...
    printf("%f", m[i][j]);
...
print_matrix( my_matrix );


fixed

annoynimous ★★★★★
()

если размерности не фиксированы заранее, то обычно эмулируется одномерным. m[i * y_size + j]

dilmah ★★★★★
()

Какой тип у my_matrix ? "Матрицу любой размерности" каким образом реализуете ? Тут будут определенные сложности, так как с собственно указателем на матрицу придется тащить и информацию о размерности и/или размерах. В вашем примере если двумерная матрица задана как массив массивов, то и передавать нужно указатель на указатель, т.е. float** m.

SLiDER
()

> float* m[]
> float**

Ни то, ни другое не работает. А жаль.
> если размерности не фиксированы заранее, то обычно эмулируется одномерным. m[i * y_size + j]

Спасибо.

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

> Ни то, ни другое не работает. А жаль.

при передаче аргумента выполни явное приведение типа

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

Реализуется? Ну к примеру
float m_1[2][2]={{1, 2}, {3, 4}}
float m_2[2][3]={{1, 2, 3}, {4, 5, 6}}
print_matrix(m_1, 2, 2);
print_matrix(m_2, 2, 3);
должно работать.

> float** m

"can`t convert 'float (*)[2]' to 'float**'"

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

> float m_1[2][2]={{1, 2}, {3, 4}}

если описывать так - то да, получается по сути одномерный массив, float** не катит, как люди писали - m[i * y_size + j]

а можно так:

float** allocate_array_float( int nr, int nc )
{
	float **arr;
	arr = (float**) malloc( nr * sizeof( float*) );
	arr[ 0 ] = (float*) malloc( nr * nc * sizeof( float ) );

	int i;
	for( i = 1 ; i < nr ; ++i ) 
		arr[ i ] = arr[ 0 ] + i * nc;

	return arr;
}

и по сути так лучше - так как ты явно хочешь использовать матрицы раного размера( иначе можно было просто описать параметр как float[5][5] например )

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

> и по сути так лучше

такой способ по скорости хуже -- там во первых косвенный доступ, во вторых локальность данных хуже.

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

На самом деле при произвольном (а не последовательном) обращении к элементам массива косвенный доступ, в случае многомерных массивов (т.е. массив массивов и т.п.), работает быстрее чем схема с умножением и сложением, для gcc примерно на 40% быстрее (для VC сейчас не помню). Ну а локальность данных, да, хуже, но тут уж ни чего не поделаешь, в крайнем случае можно свой malloc/new написать.

SLiDER
()

#include <stdio.h>

// f0 должна сама знать на этапе компиляции все размерности, кроме первой
void f0(const float m[][3]) {
	printf("%f\n", m[1][2]);
}

void f1(const float *m, int stride) {
	printf("%f\n", m[1 * stride + 2]);
}

void f2(const float *const *m) {
	printf("%f\n", m[1][2]);
}

int main(void) {
	const float m[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
	f0(m);
	// При вызове f1 должны быть известны размерности, кроме первой
	f1(m[0], 3);
	// Для вызова f2 нужен массив указателей на строки (с трёхмерными ещё хуже)
	const float *p[] = {m[0], m[1], m[2]};
	f2(p);
	return 0;
}

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

Это не матрица, а массив массивов. За такое руки отрывать надо ибо скорость доступа ниже и такое представление не совместимо ни с одной вменяемой библиотекой работы с матрицами.

Reset ★★★★★
()

Кстати, давайте уж заодно обсудим и библиотеки для работы с многомерными массивами/матрицами. Я знаю только gsl, blitz (c++) ну и... все, пожалуй

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

Тебе шашечки или ехать? Чем A[i*n+j] не устраивает? Если уж так хочется поизвращаться, то можно массив завернуть в класс, переопределить оператор [], который будет возвращать указатель на строку T *, а к нему можно применить еще один оператор [].

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

> такое представление не совместимо ни с одной вменяемой библиотекой работы с матрицами.

LOL, вы хоть код смотрели - все хранится одним блоком, так как и в обычной матрице аля float data[ 5 ][ 5 ], не знаю где вы там несовместимость увидели

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

читать умеете? речь идет о C, а не о С++, D, Java и т.п.

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

Невнимательно посмотрел, но косвенную адресацию это не отменяет. Также тебе придется еще делать специальную функцию free_matrix.

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

> Невнимательно посмотрел, но косвенную адресацию это не отменяет

проверил у себя:

float** A = allocate_array_float( 5, 5 );
for( size_t n = 0 ; n < 1000000 ; ++n )
{
for( size_t i = 0 ; i < 5 ; ++i )
for( size_t j = 0 ; j < 5 ; ++j )
A[ i ][ j ]++;
}

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

> Также тебе придется еще делать специальную функцию free_matrix.


не проблема

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

Так не очень хорошо, лучше через operator(int,int), как, собственно, во всех вменяемых библиотеках и делается.

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

> 10000x10000 сделай

0.28 сек( ес-но убрал внешний цикл - и так итераций в 4 раза больше получилось ), не верите - код очень простой, проверьте у себя

П.С. кстати я сейчас mingw3.3 собираю

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

Проверил, однохренственно.
Видимо gcc научился такое оптимизировать и выносит один указатель из внутренного цикла.


#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

double ** allocate_array( int nr, int nc )
{
	double **arr;
	arr = malloc( nr * sizeof( double *) );
	arr[ 0 ] = malloc( nr * nc * sizeof( double ) );

	int i;
	for( i = 1 ; i < nr ; ++i ) 
		arr[ i ] = arr[ 0 ] + i * nc;

	memset(&arr[0][0], 0, nr * nc * sizeof(double));

	return arr;
}

double get_full_time()
{
        struct timeval tv;
        gettimeofday(&tv, 0);
        return (double)(tv.tv_sec * 100.0 + tv.tv_usec / 10000.0);
}

#define N 15000L
int main()
{
	double ** A1 = allocate_array(N, N);
	double * A2 = malloc(N * N * sizeof(double));
	size_t i, j;
	double t1, t2;
	memset(A2, 0, N * N * sizeof(double));

	t1 = get_full_time();
	for (i = 0 ; i < N ; ++i )
		for (j = 0 ; j < N ; ++j )
			A1[i][j]++;
	t2 = get_full_time();
	printf("%lf\n", (t2 - t1) / 100.0);
	t1 = get_full_time();
	for (i = 0 ; i < N ; ++i )
		for (j = 0 ; j < N ; ++j )
			A2[i * N + j]++;
	t2 = get_full_time();
	printf("%lf\n", (t2 - t1) / 100.0);

}

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

>Проверил, однохренственно.
Видимо gcc научился такое оптимизировать и выносит один указатель из внутренного цикла.
.....

Очень странно
Program received signal SIGSEGV, Segmentation fault.


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

> согласен

1. Результаты не дают разницы ИМХО еще и потому, что оба способа уперлись в производительность обмена с памятью

2. Если у нас будет сложение 100 000 мелких размером матриц 10 на 10, то разница вполне может быть, т.к. те самые a[i] не поместятся в кэш проца, а они нам позарез нужны для вычисления a[i][j].

Параллельно сложение этих матриц придется заменять чем-то другим.

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

"Параллельное сложение" в том смысле, что сначала вычисляем result[0][0] и для этого суммируем все значения матриц[0][0]. Затем вычисляем result[0][1] ...

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

> Если у нас будет сложение 100 000 мелких размером матриц 10 на 10, то разница вполне может быть

а может и не быть, проверять надо

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

32 бита? А теперь посчитай сколько занимают массивы из теста.

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

> а может и не быть, проверять надо

а где-нибудь вариант double** будет быстрее работать или памяти меньше занимать?

З.Ы. решение чисто синтаксических проблем С такими вот (хотя и остроумными) способами не приветствую, когда есть с++

www_linux_org_ru ★★★★★
()

Мои варианты решения:

1. Лучше всего юзать С++

2. Если совсем аллергия на плюсы, юзать расширение gcc: void f( int m, int n, double x[m][n]);

3. Если все совсем плохо, написать макры для каждой функции

#define f(x) f(x, (&(x[1][0])-&(x[0][0])), sizeof(x)/sizeof(x[0][0])/(&(x[1][0])-&(x[0][0])) )

(и скобочек вокруг x добавить для надежности)

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

>1. Лучше всего юзать С++

а так хорошо всё начиналось :)

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