【问题标题】:2D matrix multiplication in cc中的二维矩阵乘法
【发布时间】:2021-08-14 10:41:48
【问题描述】:

我用 C 语言编写了一个用于矩阵乘法的代码。没有错误,但没有出现所需的输出,我的代码的哪一部分是错误的或者我错过了什么。

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

int main() {
    int r1, c1, r2, c2;
    int a[r1][c1];
    int b[r2][c2];
    int c[r1][c2];
    int i, j;
    printf("enter row1 and col1:\n");
    scanf("%d%d", &r1, &c1);
    printf("enter row2 and col2:\n");
    scanf("%d%d", &r2, &c2);
    if (c1 == r2) {
        printf("enter element of 1st matrix:");
        for (i = 0; i < r1; i++) {
            for (j = 0; j < c1; j++) {
                scanf("%d", &a[i][j]);
            }
        }
        printf("\n-----------------");
        printf("enter element of 2nd matrix:");
        for (i = 0; i < r2; i++) {
            for (j = 0; j < c2; j++) {
                scanf("%d", &b[i][j]);
            }
        }
    
        printf("the resultant matrix is:\n");
        for (i = 0; i < r1; i++) {
            for (j = 0; j < c2; j++) {
                c[i][j] += a[i][j] * b[j][i];
            }
        }
        for (i = 0; i < r1; i++) {
            for (j = 0; j < c2; j++) {
                printf("%d\t", c[i][j]);
            }
            printf("\n\n");
        }
    }
    return 0;
}

【问题讨论】:

  • 这里:int r1,c1,r2,c2; int a[r1][c1]; 此时r1c1的值是多少?数组的大小是多少?
  • 如果您使用 gcc 或 clang,请使用 -Wall -Wextra -Werror 进行编译。如果您使用的是微软的编译器,请使用/W4 /WX 进行编译。
  • 您不能(以可移植的方式)对矩阵进行维度标注,其中行和列来自动态表达式(如变量名),而是来自常量值(例如 28)。更多关于如果你使用的变量没有被初始化(这意味着它们可以有任何值)不要在公共场合说没有错误,因为你赌的是有一个(在这种情况下至少三个)

标签: arrays c 2d


【解决方案1】:

您发布的程序对来自未初始化变量的未定义值的三个矩阵进行维数。一旦您知道维度的值,您至少可以对这些变量进行维度,如(请阅读我的代码中的 cmets,因为它们在您的代码中说明了您尚未意识到的问题):

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

int main()
{
    int r1, c1, r2, c2;
    int i, j;
    printf("enter row1 and col1:\n");
    scanf("%d%d", &r1, &c1);
    printf("enter row2 and col2:\n");
    scanf("%d%d", &r2, &c2);
    int a[r1][c1];  /* declare the dimensions <<<after>>> you know what values are */
    int b[r2][c2];  /* to be given. */
    int c[r1][c2];

    if (c1 == r2) { /* put an else statement to this if indicating the cause of */
            /* not making any calculation in case r2 and c1 not being equal */
        printf("enter element of 1st matrix:");
        for (i = 0; i < r1; i++) {
            for (j = 0; j < c1; j++) {
                scanf("%d", &a[i][j]);
            }
        }
        printf("\n-----------------");
        printf("enter element of 2nd matrix:");
        for (i = 0; i < r2; i++) {
            for (j = 0; j < c2; j++) {
                scanf("%d", &b[i][j]);
            }
        }
    
        printf("the resultant matrix is:\n");
        for (i = 0; i < r1; i++) {
            for (j = 0; j < c2; j++) { /* you need three nested loops, not two */
                /* ALL YOUR CODE HERE IS BAD, USE THIS INSTEAD */
                int k;
                c[i][j] = 0; /* initialize c[i][j] to zero or you'll get weird values */
                for (k = 0; k < c1; k++) { /* YOU NEED ANOTHER LOOP */
                    c[i][j] += a[i][k] * b[k][j];
                }
            }
        }
        for (i = 0; i < r1; i++) {
            for (j = 0; j < c2; j++) {
                printf("%d\t", c[i][j]);
            }
            printf("\n\n");
        }
    }
    return 0;
}

但是为矩阵提供最大维度并仅在由r1c1r2c2r3c3 的值分隔的子框中工作更加便携。如:

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

#define MAX_DIM 10

int main()
{
    int r1, c1, r2, c2;
    int i, j; /* why didn't you call these r and c (for row and column resp.)? */

    /* this will loop on input until you give correct values for all dimensions, */
    do {
        printf("enter row1 and col1:\n");
        scanf("%d%d", &r1, &c1);
        printf("enter row2 and col2:\n");
        scanf("%d%d", &r2, &c2);
    } while (r1 <= 0 || r1 > MAX_DIM || c1 <= 0 || c1 > MAX_DIM ||
             r2 <= 0 || r2 > MAX_DIM || c2 <= 0 || c2 > MAX_DIM ||
             c1 != r2);

    int a[MAX_DIM][MAX_DIM]; /* more portable this way, as variable dimensioning */
    int b[MAX_DIM][MAX_DIM]; /* is not present in all standard editions */
    /* there's no need to have matrix c as we are not doing anything with it. 
     * (see below why)*/

    printf("enter element of 1st matrix:");
    for (i = 0; i < r1; i++) {
        for (j = 0; j < c1; j++) {
            scanf("%d", &a[i][j]);
        }
    }

    printf("\n-----------------");

    printf("enter element of 2nd matrix:");
    for (i = 0; i < r2; i++) {
        for (j = 0; j < c2; j++) {
            scanf("%d", &b[i][j]);
        }
    }
    
    printf("the resultant matrix is:\n");
    for (i = 0; i < r1; i++) {
        for (j = 0; j < c2; j++) {
            int k;
            int cell_value = 0;
            for (k = 0; k < c1 /* or k < r2, both are the same */; k++) {
                    cell_value += a[i][k] * b[k][j];
            }
            /* cell_value is the value of product matrix at [i][j] */
            printf("%d\t", cell_value);
        }
        printf("\n\n"); /* new line for each row */
    }
    return 0;
}

如您所见,最后一个示例不需要存储产品矩阵 c,因为您可以在计算元素时打印元素(按行生成,然后按列生成)。

【讨论】:

  • 如果每个维度的最大尺寸是可以接受的,那么您的解决方案就是优雅的。
  • 您还可以通过在 do / while 循环之前移动矩阵定义来提高对 C99 之前的编译器的可移植性。
  • nope.... 分配一个由 0 个元素组成的数组将不会给您提供工作的存储区域。所以维度必须至少为 1。
  • 哦...没错...对不起...我的错误... :) 已编辑。现在应该是正确的。
【解决方案2】:

代码中存在多个问题:

  • 数组int a[r1][c1];int b[r2][c2];int c[r1][c2];是在用户读取r1c1之前定义的:代码具有潜在的未定义行为,因为r1c1未初始化,因此具有不确定的值:如果大小恰好为负数,数组的分配可能会失败或导致未定义的行为,并且使用迭代到用户输入的不同边界值的索引值访问它们将具有未定义的行为。

  • 您验证a 的列数等于b 的行数,但您还应该检查所有维度是否为正,以避免潜在的未定义行为。

  • 将矩阵定义为具有自动存储的可变长度数组是有风险的:大尺寸可能会导致堆栈溢出。建议从堆中分配矩阵。

  • 乘法算法不正确:必须实现三重循环,在内循环之前将目标元素初始化为c1[i][j]

这是修改后的版本:

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

int main() {
    int r1, c1, r2, c2;
    printf("enter row1 and col1:\n");
    if (scanf("%d%d", &r1, &c1) != 2)
        return 1;
    printf("enter row2 and col2:\n");
    if (scanf("%d%d", &r2, &c2) != 2)
        return 1;
    if (r1 <= 0 || c1 <= 0 || r2 <= 0 || c2 <= 0 || c1 != r2) {
        printf("invalid matrix sizes\n");
        return 1;
    } else {
#if !defined ALLOCATE_MATRICES_FROM_THE_HEAP
        // if the dimensions are small, you can define the matrices as
        int a[r1][c1], b[r2][c2], c[r1][c2];
#else
        // for large sizes, you can allocate the matrices from the heap this way:
        int (*a)[c1] = calloc(sizeof(*a), r1);
        int (*b)[c2] = calloc(sizeof(*b), r2);
        int (*c)[c2] = calloc(sizeof(*c), r1);
        if (a == NULL || b == NULL || c == NULL) {
            printf("out of memory\n");
            return 1;
        }
#endif
        printf("enter elements of 1st matrix:");
        for (int i = 0; i < r1; i++) {
            for (int j = 0; j < c1; j++) {
                a[i][j] = 0;
                scanf("%d", &a[i][j]);
            }
        }
        printf("\n-----------------");
        printf("enter elements of 2nd matrix:");
        for (int i = 0; i < r2; i++) {
            for (int j = 0; j < c2; j++) {
                b[i][j] = 0;
                scanf("%d", &b[i][j]);
            }
        }
    
        printf("the resultant matrix is:\n");
        for (int i = 0; i < r1; i++) {
            for (int j = 0; j < c2; j++) {
                int v = 0;
                for (int k = 0; k < c1; k++) {
                    v += a[i][k] * b[k][j];
                }
                c[i][j] = v;
            }
        }
        for (int i = 0; i < r1; i++) {
            for (int j = 0; j < c2; j++) {
                printf("%5d\t", c[i][j]);
            }
            printf("\n\n");
        }
#if defined ALLOCATE_MATRICES_FROM_THE_HEAP
        free(a);
        free(b);
        free(c);
#endif
        return 0;
    }
}

【讨论】:

  • 非常感谢,我刚开始学习代码,我的路上会遇到一些小问题。
  • @KiranRai:很高兴为您提供帮助。您可以通过单击其分数下方的灰色复选标记来接受其中一个答案。
  • 未定义的行为不是潜在的,这确实是一个事实! :)
  • 为什么要以如此复杂的方式分配矩阵?您声明一个可变数量的参数数组(这是您在给出的解释中试图避免的)以动态分配动态数量的整数,您以后必须释放。
  • @LuisColorado:维度的值是不确定的,定义具有负数或零大小或大于本地约束允许的大小的数组具有未定义的行为,因此 UB 很可能在那个阶段,并且如果不是这样,如果用户输入的实际尺寸与实际定义不符,就会出现另一种UB,进一步增加了UB发生的可能性,极有可能。此修订是在之前与 Ricci 的讨论之后进行的。
【解决方案3】:

问题出在这里:

int r1, c1, r2, c2;
int a[r1][c1];   // at this point r1 c1, r2, c2 etc.
int b[r2][c2];   // are not yet initialized and their
int c[r1][c2];   // content is undetermined. Therefore
                 // the size of the arrays a, b and C are
                 // undetermined leading to the problem you encounter

你想要这个:

int r1,c1,r2,c2;
int i,j;
printf("enter row1 and col1:\n");
scanf("%d%d",&r1,&c1);
printf("enter row2 and col2:\n");
scanf("%d%d",&r2,&c2);

int a[r1][c1];  // here r1, c1, r2, c2 etc.
int b[r2][c2];  // have determined values
int c[r1][c2];

您的代码中可能还有更多问题,我没有检查。

【讨论】:

  • @KiranRai 您应该接受其中一个答案。 chqrlie 的答案是最好和最完整的 IMO。
【解决方案4】:
#include<stdio.h>
#include<stdlib.h>
int main(){
    int r1,c1,r2,c2;
   
    int i,j;
    printf("enter row1 and col1:\n");
    scanf("%d%d",&r1,&c1);
    printf("enter row2 and col2:\n");
    scanf("%d%d",&r2,&c2);
     int a[r1][c1];
    int b[r2][c2];
    int c[r1][c2];
    if(c1==r2){
        printf("enter element of 1st matrix:");
        for(i=0;i<r1;i++){
            for(j=0;j<c1;j++){
                scanf("%d",&a[i][j]);
            }
        }
        printf("\n-----------------");
        printf("enter element of 2st matrix:");
        for(i=0;i<r2;i++){
            for(j=0;j<c2;j++){
                scanf("%d",&b[i][j]);
            }
    }
    
    printf("the resultant matrix is:\n");
    for(i=0;i<r1;i++){
        
        for(j=0;j<c2;j++){
            c[i][j]=0;
            for(int k=0;k<c1;k++)    
              {    
           c[i][j]+=a[i][k]*b[k][j];    
              } 
        }
    }
    for(i=0;i<r1;i++){
        for(j=0;j<c2;j++){
            printf("%d\t",c[i][j]);
        }
        printf("\n\n");
    }
    
    
}
return 0;
    
    
}

【讨论】:

  • 没有解释的代码不是一个好的答案。
  • 是的,这是我的第一篇文章,我无法在其中添加标签。