Я хотел бы написать функции, которые выполняют общие матричные операции. Это можно сделать с помощью двумерных массивов или арифметики указателей. Я предпочитаю версию с указателем. Теперь с указателями я бы написал такую функцию:
void matmult(double *a, double *b, double *c, int m, int n, int k);
Проблема в том, что я должен использовать приведение, когда я передаю 2-мерные массивы в функцию. Есть ли хорошее решение, чтобы избежать этой проблемы?
Работает без приведения (конечно), но я хочу избежать предупреждений компилятора.
Обновление: массивы определены как 2-мерный массив, а вызывающая функция выглядит так:
// M, N, K are constants
double a[M][N];
double b[N][K];
double c[M][K];
matmult((double *)a, (double *)b, (double *)c, M, N, K);
Функция matmult — это прямая реализация матричного умножения (три вложенных цикла for с использованием указателей).
*(c + i*k + j) += *(a + i*n + p) * *(b + p*k + j);
Я просто хочу избавиться от гипса.
Почему бы вам не хранить размеры матрицы в самой матрице, а также в массиве чисел; это то, что делает математика.
@Neil Что бы ты сделал? Обратите внимание, что размеры матрицы не являются проблемой, поскольку вызывающая сторона должна убедиться, что они имеют правильные размеры. Проблема заключается в несоответствии типов между double * и (*doube)[].
static void matmult(double a[][N], double (*b)[K], double c[M][K], int m, int n, int k);
Этот прототип работает для передачи двумерных массивов. См. эту запись C FAQ или Массив для уменьшения указателя и передачи многомерных массивов функциям. M
в c[M][K]
совершенно лишний в C, но может быть полезен в качестве самостоятельной документации (или может еще больше запутать читателя).
Однако это не очень инкапсулировано, и я бы не стал программировать с этим общий матричный алгоритм. Размер является неотъемлемой частью самой матрицы. Я мог бы использовать элемент гибкого массива C99 для хранения размера и данных вместе, если он не является частью других структур.
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <limits.h>
struct matrix {
unsigned x, y;
double data[]; /* flexible array member */
};
/** Constructor; data is uninitialized. */
static struct matrix *matrix(const unsigned x, const unsigned y) {
if (!x || !y || x >= UINT_MAX / y) { errno = ERANGE; return 0; }
struct matrix *const m = malloc(offsetof(struct matrix, data)
+ sizeof *m->data * x * y);
if (!m) return 0;
m->x = x, m->y = y;
return m;
}
/** Constructor; IBM has a useful extension that allows stack construction. */
static struct matrix *matrix_init(const unsigned x, const unsigned y, ...) {
struct matrix *const m = matrix(x, y);
if (!m) return 0;
va_list argp;
va_start(argp, y);
for(unsigned i = 0, size = x * y; i < size; i++)
m->data[i] = va_arg(argp, double);
va_end(argp);
return m;
}
static void matrix_print(const struct matrix *const m) {
if (!m) { printf("null\n"); return; }
for(unsigned y = 0; y < m->y; y++) {
printf("[");
for(unsigned x = 0; x < m->x; x++)
printf("%s%4.f", x ? ", " : "", m->data[y * m->x + x]);
printf("]\n");
}
}
static struct matrix *matmult(const struct matrix *a,
const struct matrix *b) {
if (!a || !b || a->y != b->x) { errno = EDOM; return 0; }
struct matrix *const c = matrix(b->x, a->y);
if (!c) return 0;
for(unsigned y = 0; y < a->y; y++)
for(unsigned x = 0; x < b->x; x++)
c->data[y * b->x + x] = 0.0;
/* implement:
*(c + i*k + j) += *(a + i*n + p) * *(b + p*k + j); */
return c;
}
int main(void) {
struct matrix *a = 0, *b = 0, *c = 0;
int success = EXIT_SUCCESS;
if (!(a = matrix_init(2, 2, 1.0, 2.0, 3.0, 4.0))
|| !(b = matrix_init(2, 2, 3.0, 20.0, 1.0, 0.0))) goto catch;
matrix_print(a), printf("*\n"), matrix_print(b);
if (!(c = matmult(a, b))) goto catch;
printf("=\n"), matrix_print(c);
goto finally;
catch:
success = EXIT_FAILURE;
perror("matrix");
finally:
free(a), free(b), free(c);
return success;
}
Вот как я наконец решил свою проблему благодаря подсказке @Neil:
void matrixmult(double (*a)[], double (*b)[], double (*c)[], int m, int n, int k)
{
for (...)
for (..)
for (...)
*((double *)c + i*k + j) = ...
}
Теперь функцию можно вызывать без приведения, и я избегаю ограничений функции C99 VLA (сначала должны быть переданы размеры).
ну, это зависит от того, как выглядит ваш 2d-массив, покажите код вызова и покажите код в matmult