0

While trying to multiply 2 matrices

void Multiply(float* A, float* B, float* C)
{
    int m = sizeof(A) / sizeof(A[0]);
    int p = sizeof(A[0]) / sizeof(A[0][0]);
    int n = sizeof(B) / sizeof(B[0]);

    int i, j, k;
    for (i = 0;i < m;i++)
        for (j = 0;j < n;j++)
        {
            C[n*i + j] = 0;
            for (k = 0;k < p;k++)
                C[n*i + j] = C[n*i + j] + A[p*i + k] * B[n*k + j];
        }
}

I get the error invalid types 'float[int]' for array subscript at this line

int p = sizeof(A[0]) / sizeof(A[0][0]);

What am i doing wrong?

OrElse
  • 9,709
  • 39
  • 140
  • 253
  • 2
    What is `A[0][0]`? What did you try to achieve by that? In any case, the `sizeof` trick is pointless in this context. This trick only works with arrays. – AnT stands with Russia May 23 '19 at 17:51
  • 2
    `sizeof(A)` is a size of a pointer, not an array. – Eugene Sh. May 23 '19 at 17:52
  • 1
    This is partly a duplicate of [this](https://stackoverflow.com/questions/8269048/length-of-array-in-function-argument), but, given the misconception about two-dimensional arrays, it is a superset. – Eric Postpischil May 23 '19 at 17:55

1 Answers1

2

In the routine that calls Multiply, A might be defined as float A[X][Y];. However, Multiply receives only a pointer, not an array. Also, Multiply receives no information about the sizes of the original arrays, so they cannot be calculated in the subroutine. They must be passed by the caller.

When an array is used in an expression, including a function argument but excluding the operand of sizeof or unary & (or a string literal used to initialize an array), it is automatically converted to a pointer to its first element.

If A is an array of X arrays of Y float, then the first element of A is an array of Y float. Therefore, when A is used as a function argument, it is automatically converted to the type float (*)[Y]. This is different from the parameter declaration, which is float *. A pointer to an array of float is not compatible with a pointer to a float, and your compiler should warn you about that.

If you nonetheless compile the program with warnings ignored, or a cast to override the types, then Multiply has only a float * for A. It has no information about the size of the array or the fact that is an array of arrays. Then, in the expression sizeof(A) / sizeof(A[0]) (in which the parentheses are unnecessary), sizeof(A) is the size of a pointer to float, because A is a pointer to float, and sizeof(A[0])is the size of afloat, becauseA[0]is afloat(as it must be sinceAinMultiplyis afloat *`).

The parameter B has the same problem with the size calculation. In Multiply, B is only a pointer to float, size sizeof(B) / sizeof(B[0]) calculates only the size of a pointer divided by the size of a float. It does not provide the size of the original array.

Oddly, your code uses A both as a two-dimensional array, in A[0][0], and a one-dimensional array, in A[p*i + k]. You must choose one of these.

If your compiler supports variable length arrays, then you can declare Multiply as:

void Multiply(int m, int p, int n, float (*A)[p], float (*B)[n], float (*C)[n])

Then you can use A[i][k], B[k][j], and C[i][j] in the routine. In the calling routine, you would write the original arrays as arguments, as with Multiply(X, Y, Z, A, B, C), where X, Y, and Z are the necessary dimensions.

An alternative is to pass the arrays as pointers to float instead of pointers to variable length arrays:

void Multiply(int m, int p, int n, float *A, float *B, float *C)

Then you would use A[p*i + k] and such as your current code shows. In the calling routine, you would pass pointers to the [0][0] elements of the arrays: Multiply(X, Y, Z, &A[0][0], &B[0][0], &C[0][0]).

Pedantically, this may have aliasing and pointer arithmetic problems since the resulting code accesses float elements using index calculations outside the nominal arrays. It is commonly supported in compilers, but you ought to check.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312