Reputation: 1
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++;
}
}
Upvotes: 0
Views: 174
Reputation: 123568
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
Upvotes: 0
Reputation: 14157
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);
Upvotes: 0
Reputation: 67855
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:
Upvotes: 2