0

I have two 2D arrays that look like this, int A[3][3] and int B[3][3], and I have to multiply them. Can I do it like this? Make a function and just send them to it? I know I can but is this how you multiply 2D arrays?

void mult(int *C,int *A,int *B){
    for(int i = 0;i<9;i++){
        *C = (*A) * (*B);
        C++;
        A++;
        B++;
    }
}
Null
  • 1,950
  • 9
  • 30
  • 33
  • Have you tested it? – kaylum Feb 18 '21 at 19:53
  • This *might* be better on the Code Review SE. –  Feb 18 '21 at 19:54
  • Are you asking if this is valid according to the language, or if this is good style? – Barmar Feb 18 '21 at 19:55
  • @kaylum "I know I can" suggests that he tried it and it worked. But lots of code with UB seems to work sometimes, so that's not a good test. – Barmar Feb 18 '21 at 19:56
  • 3
    You might want to read up [Matrix Multiplication](https://en.wikipedia.org/wiki/Matrix_multiplication) if that's what you're asking, as what you're doing isn't that. – Priyanshul Govil Feb 18 '21 at 19:57
  • if its good according to style cuz i saw that someone did it like this ` void mult(int *a,int *b,int *c){ for(int i=0;i<3;i++){ *(c+i)=*(a)**(b+i)+*(a+1)**(b+i+3)+*(a+2)**(b+i+6); } for(int i=0;i<3;i++){ *(c+i+3)=*(a+3)**(b+i)+*(a+1+3)**(b+i+3)+*(a+2+3)**(b+i+6); } for(int i=0;i<3;i++){ *(c+i+6)=*(a+6)**(b+i)+*(a+1+6)**(b+i+3)+*(a+2+6)**(b+i+6); } } ` – plana cs16 Feb 18 '21 at 19:57
  • You have an elementwise multiplication here which is not the same as matrix multiplication, if you are looking for it. – Eugene Sh. Feb 18 '21 at 19:59
  • @EugeneSh. yes im looking for matrix mulitplication – plana cs16 Feb 18 '21 at 20:00
  • 1
    Then the answer to your question is -no, this is not how you multiply matrices. – Eugene Sh. Feb 18 '21 at 20:02
  • @EugeneSh. Can u direct me to somewhere where i can learn it,i get it how u do it in math but I cant wrap my head around how to do it in C – plana cs16 Feb 18 '21 at 20:04
  • Just google "matrix muliplication in C" and you will get plenty of relevant results with ready-to-use codes. – Eugene Sh. Feb 18 '21 at 20:06
  • @EugeneSh. Ok thank you very much – plana cs16 Feb 18 '21 at 20:08
  • Can I do it like this? --> it is OK as long as `C` does not overlap `A,B`. – chux - Reinstate Monica Feb 18 '21 at 20:57

3 Answers3

2

Elementwise multiplication done this way is OK. If you use indexes for some reason gcc does not want to emit vector code.

void mult(int C[3][3],int A[3][3],int B[3][3]){
    for(int i = 0;i<3;i++)
        for(int j = 0;j<3;j++)
        {
            C[i][j] = A[i][j] * B[i][j];
        }
}

void mult1(int *C,int *A,int *B){
    for(int i = 0;i<9;i++){
        *C = (*A) * (*B);
        C++;
        A++;
        B++;
    }
}

but the code generated for this small arrays is different in both cases:

https://godbolt.org/z/Gr31Tf

0___________
  • 60,014
  • 4
  • 34
  • 74
0

You could use VLAs:

void mult(int r, int c, int C[r][c], int A[r][c], int B[r][c]) {
    for(int i = 0; i < r; i++)
        for(int j = 0; j < c; j++)
            C[i][j] = A[i][j] * B[i][j];
}
...

int A[3][3], B[3][3], C[3][3];
mult(3, 3, C, A, B); 
tstanisl
  • 13,520
  • 2
  • 25
  • 40
0

We need to be really clear what you mean here - do you mean you want to multiply each a[i][j] by each b[i][j] and assign the result to c[i][j], such that

+---+---+   +---+---+   +-----+-----+     +----+----+
| 1 | 2 |   | 5 | 6 |   | 1*5 | 2*6 |     |  5 | 12 |
+---+---+ x +---+---+ = +-----+-----+  =  +----+----+
| 3 | 4 |   | 7 | 8 |   | 3*7 | 4*8 |     | 21 | 32 |
+---+---+   +---+---+   +-----+-----+     +----+----+

Or are you trying to implement matrix multiplication where

+---+---+   +---+---+   +-----------+-----------+   +----+----+
| 1 | 2 |   | 5 | 6 |   | 1*5 + 2*7 | 1*6 + 2*8 |   | 19 | 22 |
+---+---+ x +---+---+ = +-----------+-----------+ = +----+----+
| 3 | 4 |   | 7 | 8 |   | 3*5 + 4*7 | 3*6 + 4*8 |   | 43 | 50 |
+---+---+   +---+---+   +-----------+-----------+   +----+----+

which is very different?

If the former, then what you've written is mostly okay depending on how you're calling the function - remember that under most circumstances, an expression of type "N-element array of T" will "decay" to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. An expression of type "3-element array of 3-element array of int" (int [3][3]) will decay to type "pointer to 3-element array of int", (int (*)[3]), not int *.

If you declare your arrays as

int a[3][3], b[3][3], c[3][3];

and call the function as

mult( &a[0][0], &b[0][0], &c[0][0] ); // explicitly pass a pointer to the
                                      // first element of each array

then what you have is fine. A little ugly, but fine.

If, OTOH, you're calling the function as

mult( a, b, c );

then what you have is not fine because the argument types don't match up (and the compiler should yell at you over it). In order for the argument types to match up, your function prototype will need to be one of

void mult( int A[3][3], int B[3][3], int C[3][3] )

or

void mult( int A[][3], int B[][3], int C[][3] )

or

void mult( int (*A)[3], int (*B)[3], int (*C)[3] )

which all mean the same thing in the context of a function parameter declaration1. But this also means you can't "walk" through the arrays just by incrementing and dereferencing the pointer, because you'd basically wind up iterating over A[0][0], A[1][0], and A[2][0], and you'd get a type error because *A is not an int, it's an int [3]. The actual meat of the code would need to be

for ( size_t i = 0; i < 3; i++ )
  for ( size_t j = 0; j < 3; j++ )
    C[i][j] = A[i][j] * B[i][j];

When dealing with arrays, especially multi-dimensional arrays, use array subscript notation rather than trying to muck with pointers or pointer arithmetic - that's what it's there for, and you're less likely to make a mistake.


Matrix multiplication is a different matter - each C[R][V] is the sum of A[R][x] * B[x][V] for all x, and trying to do that with just pointer arithmetic is masochistic. For that, you'd want to do something like:

for ( size_t R = 0; R < 2; R++ )
{
  for ( size_t V = 0; V < 2; V++ )
  {
    for ( size_t i = 0; i < 2; i++ )
    {
      C[R][V] += A[R][i] * B[i][V]; // assumes C[R][V] has already been initialized to 0
    }
  }
}

For a general purpose matrix multiplication function, where you don't just have 3x3 matrices, something like this might work (assumes variable-length arrays are supported, which if you're working on a C99 or later implementation they should be):

void mat_mul_v( size_t ar, size_t ac, int a[ar][ac],
                size_t br, size_t bc, int b[br][bc],
                size_t cr, size_t cc, int c[cr][cc] )
{
  assert( ar == bc );  // number of rows in a must equal number of columns in b
  assert( ac == br );  // likewise, number of columns in a must equal number of rows in b
  assert( ar == cr );  // c must have the same number of rows as a
  assert( bc == cc );  // and the same number of columns as b

  memset( c, 0, sizeof *c * cr );

  for ( size_t R = 0; R < cr; R++ )
  {
    for ( size_t V = 0; V < cc; V++ )
    {
      for ( size_t i = 0; i < ac; i++ )
      {
        c[R][V] += a[R][i] * b[j][V];
      }
    }
  }
}

Small test program:

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

void mat_mul_v( size_t ar, size_t ac, int a[ar][ac],
                size_t br, size_t bc, int b[br][bc],
                size_t cr, size_t cc, int c[cr][cc] )
{
  assert( ar == bc );
  assert( ac == br );
  assert( ar == cr );
  assert( bc == cc );

  memset( c, 0, sizeof *c * cr );

  for ( size_t R = 0; R < cr; R++ )
  {
    for ( size_t V = 0; V < cc; V++ )
    {
      for ( size_t i = 0; i < ac; i++ )
      {
        c[R][V] += a[R][i] * b[i][V];
      }
    }
  }
}

int main( void )
{
  int a[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; // 1  2  3    1  2  3    30  36  42
  int b[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; // 4  5  6    4  5  6    42  81  96
  int c[3][3];                             // 7  8  9    7  8  9   102 126 150

  int d[2][3] = {{1,2,3}, {4,5,6}};   //  1  2  3      1  2    22 28
  int e[3][2] = {{1,2},{3,4},{5,6}};  //  4  5  6  x   3  4  = 49 64
  int f[2][2];                        //               5  6

  mat_mul_v( 3, 3, a, 3, 3, b, 3, 3, c );

  for( size_t i = 0; i < 3; i++ )
  {
    for ( size_t j = 0; j < 3; j++ )
    {
      printf( "%4d ", c[i][j] );
    }
    putchar( '\n' );
  }

  putchar( '\n' );

  mat_mul_v( 2, 3, d, 3, 2, e, 2, 2, f );
  
  for ( size_t i = 0; i < 2; i++ )
  {
    for ( size_t j = 0; j < 2; j++ )
    {
      printf( "%4d ", f[i][j] );
    }
    putchar( '\n' );
  }

  return 0;
}

And the output:

  30   36   42 
  66   81   96 
 102  126  150 

  22   28 
  49   64 

  1. And only for function parameter declarations - for regular declarations, they mean very different things.
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • How would calling the function with, e.g., `&a[0][0]` and iterating through 9 array members be fine? There is an example in [Annex J.2](https://port70.net/~nsz/c/c11/n1570.html#J.2) that seems to point out that exactly this is UB: "_An array subscript is out of range, even if an object is apparently accessible with the given subscript (as in the lvalue expression a[1][7] given the declaration int a[4][5])._" [The consensus here](https://stackoverflow.com/questions/6290956/one-dimensional-access-to-a-multidimensional-array-is-it-well-defined-behaviour) seems to be that you can't do this. – ad absurdum Feb 19 '21 at 18:09