简体   繁体   中英

Can you multiply two 2d arrays like this in C?

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++;
    }
}

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

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); 

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 declaration 1 . 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM