【问题标题】:Trying to write a matrix multiplier for arbitrary-sized matrices尝试为任意大小的矩阵编写矩阵乘法器
【发布时间】:2011-09-16 18:13:43
【问题描述】:

尝试在 C 中为任意大小的矩阵编写矩阵乘法函数。我正在尝试以下函数签名:

void matrixMult(void *A, int Xa, int Ya, void *B, int Xb, int Yb);

(在我对其进行故障排除时,它现在返回 void,一旦它按照我想要的方式工作,我将在稍后进行适当的返回)

X 和 Y 参数旨在告知函数传入数组的维度(据我了解,这是必要的,因为在 C 中数组不知道自己的大小)。所以我传入了两个 void 指针 A 和 B,以及它们的尺寸。

我的问题: 进入函数后,如何将 void 指针转换回 int 数组以便读取它们?我尝试了以下方法:

(int)*A[someX][someY]

,但我收到有关“无效指针的无效使用”的编译器错误。


编辑

以下是我目前的全部功能(在我对其进行故障排除时进行更新):

#pragma warning(disable: 8057)
#include <stdio.h>

void matrixMult(int **A, int Xa, int Ya, int **B, int Xb, int Yb);

int main(int argc, char *argv[]) {
    //   [x][y] x--> row; y-->cols
    int a[3][5] = {
          {  1,  2,  3,  4,  5 }
        , { 10, 20, 30, 40, 50 }
        , {  4,  8, 15, 16, 23 }
    };
    int b[5][7] = {
          {  1,  2,  3,  4,  5,  6,  7 }
        , { 10, 20, 30, 40, 50, 60, 70 }
        , {  4,  8, 15, 16, 23, 42,  0 }
        , {  1,  2,  3,  4,  5,  6,  7 }
        , { 10, 20, 30, 40, 50, 60, 70 }
    };

    matrixMult((int **)a, 3, 5, (int **)b, 5, 7);

    printf("done\n");
    return 0;
}

void matrixMult(int **A, int Xa, int Ya, int **B, int Xb, int Yb) {
    printf("starting matrix mult\n");
    int i, j;

    for (i=0; i<Xa; i++) {
        for (j=0; j<Ya; j++) {
            //printf("%i, %i:\t", i, j);
            printf("%i\t", A[Ya*i+j]);
        }
        printf("\n");
    }
}

编辑 2 感谢您提供提示和资源!上面发布的版本正如我现在所期望的那样工作,但我担心它是如何使用指针的丑陋混蛋。现在我要对指针做更多的阅读(感谢 cnicutar!),稍后我会重新讨论。对我的最新版本的任何进一步批评表示赞赏,但我将其标记为已回答并继续进行进一步阅读。再次感谢!

【问题讨论】:

  • 使用该签名,您不能使用双数组表示法,因为编译器不知道数组行有多大。您将不得不使用指针算法。
  • int[5][7] 更像int* 而不是int**。将您的函数更改为int*,并在访问元素时使用a[x+y*width]
  • @Banthar 但那会是 UB 吗?更不用说不清楚了。
  • @Daniel:我认为没有其他方法可以动态访问静态多维数组。
  • 由于他的维数是常数(2D),所以永远是双指针。

标签: c arrays matrix


【解决方案1】:

如果我理解你的问题你可以做类似的事情

int **a = A;

你为什么直接使用void *而不是int **A

编辑 1

根据以下评论:请参阅this FAQ entryf3 是您想要的)。还有this one

编辑 2

像这样声明和初始化它们(一些东西):

int **a;

a = malloc(sizeof(*a) * 3);
for (i = 0; i < 3; i++) {
    a[i] = malloc(sizeof(int) * 5);
}

【讨论】:

  • 使用 void * 因为我没有经验@ C :-} 我想要的是一种传递任意大小数组的方法。所以我可以改变信号。那么,使用 int **A 来传递数组呢?正在尝试...
  • 不,您不能传递任意大小的 n 维数组。您可以传递指针:-)
  • 现在阅读该常见问题解答;见我上面的编辑;现在我在 sig 中使用 int **A,在函数调用中使用 &a;我在编译器中遇到不兼容的类型错误。感谢您的常见问题解答链接!编辑:哇!不相称的。类型错误是因为我也没有更新函数定义。
  • @loneboat 阅读这些内容,并阅读c-faq.com/aryptr/pass2dary.html。实际上,有一天阅读所有列表会产生奇迹(对我来说确实如此)。
  • 非常感谢那些 - 我想你让我走上了正轨!我在 PHP lala 的土地上已经好几年了,并且正在尝试重新拾取 C。
【解决方案2】:

原型必须是双指针。此外,您不需要取消引用(除非您最里面的指针是指针本身),因为您将它转换为 int。除非类型是 int *。

void matrixMult(void **A, int Xa, int Ya, void **B, int Xb, int Yb);

不需要取消引用,因为它只是一个基本的 int。

(int)A[someX][someY]

如果你有,假设一个 int * 你需要取消引用它,但即使那样你也会在演员表之外取消引用

*(int *)A[someX][someY]

在取消引用指针之前,必须让编译器知道类型是什么。否则你会得到一个试图取消引用 void * 的错误。

【讨论】:

  • 或者他可以只使用一个 int** 并保存自己的演员表。
【解决方案3】:

如果你有 C99,你可以简单地使用可变长度数组,它们就是为此而生的。忘记指向指针的指针,只做

void matrixMult(size_t n, size_t m, size_t k, double A[n][m], double B[m][k], double C[n][k]);

这里重要的是,大小在参数列表中排在第一位,这样在涉及ABC 时就可以知道它们。在您的函数中,您可以通过 A[i][j] 之类的方式访问各个矩阵元素。

然后要分配这样的矩阵,您必须小心不要将它们分配到堆栈上。例如

double (*A)[n][m] = malloc(*A);

应该这样做。

真正尝试忘记将矩阵定义为指针数组,其中每个指针都必须分配自己的块等等。这太复杂了,容易出错,并且增加了不必要的间接级别。

【讨论】:

    【解决方案4】:

    void* 不是某种模板类型参数。由于您需要将指针转换为某种类型,即指针指向的数据类型,并且整个操作都是针对该类型编译的,因此单独使用 void* 指针在这里意义不大。要么添加一个参数来告诉传递哪种类型,然后相应地在函数中分支,或者更好地使用正确的类型并相应地命名函数。此外,您不应该使用双指针:根据内存分配,它可能会破坏缓存局部性。

    要节省打字使用宏:

    #define MATRIXMULT(type, typeshort) void matrixMult##typeshort(\
    ##type *A, int const Xa, int const Ya, ##type *B, int const Xb, int const Yb)\
    { \
        printf("starting matrix mult\n"); \
        int i, j, k; \
    \
        if(Ya != Xb || Xa != Yb) \
            return; // dimensions don't match \
    \
        for (i=0; i<Xa; i++) { \
            for (j=0; j<Ya; j++) { \
                A[i*Ya + j]=0; \
                for(k=0; k < Xa; k++) \
                    A[i*Ya + j] += A[i*Ya + k] * B[k*Yb + j]; \
        } \
    }
    
    MATRIXMULT(double, d)
    MATRIXMULT(float, f)
    MATRIXMULT(int, i)
    

    因评论而编辑:

    MATRIXMULT(double, d) 扩展为

    void matrixMultd(
    double *A, int const Xa, int const Ya, double *B, int const Xb, int const Yb)
    { 
        printf("starting matrix mult\n"); 
        int i, j, k; 
    
        if(Ya != Xb || Xa != Yb) 
            return;
    
        for (i=0; i<Xa; i++) { 
            for (j=0; j<Ya; j++) { 
                A[i*Ya + j]=0; 
                for(k=0; k < Xa; k++) 
                    A[i*Ya + j] += A[i*Ya + k] * B[k*Yb + j]; 
        } 
    }
    

    因此,使用宏很容易在多个变体中获得相同的功能。通过上述宏扩展,您可以调用matrixMultdmatrixMultfmatrixMulti

    【讨论】:

    • 有趣 - 你能详细说明一下宏是如何工作的吗?我很少使用宏,但它看起来很方便。谢谢!
    • @loneboat:这只是一点点预处理器的魔法。在此位置写入 MATRIXMULT 时,将粘贴宏的内容,将宏参数放在宏中出现的任何位置。 ## 是标记连接,因此您在参数中写入的内容将在那里连接。
    猜你喜欢
    • 2013-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-19
    相关资源
    最近更新 更多