How to interpret this function definition? How should I pass arguments to it?
void matmul(float (*A)[N],int BlockX, int BlockY)
The first argument is a pointer to an array of N elements:
float a[N];
matmul(&a, 2, 3);
(Note that N has to be a compile-time constant in C89 and C++; in C89 it would essentially have to be #defined as some literal value. In C99 you have variable-length arrays.)
Since arrays decay to pointers, you can also feed it an array:
float b[M][N];
matmul(b, 2, 3);
Another way of writing the same prototype would be
void matmul(float A[][N],int BlockX, int BlockY)
which better shows what this usually supposed to receive, a two dimensional array, for which N is
a compile time integer constant (not a const variable!) if you only have C89
any integer expression which can be evaluated at the point of the definition if you have modern C99
The other dimension is not specified and you have to know or transmit it somehow.
It looks to me that this interface is an oldish one, since it seems to use int parameters to pass size information. The modern way to do this (and avoid 32/64 bit problems and stuff like that) would be to use size_t for such quantities.
If by chance the two parameters would correspond to the "real" matrix dimension, in modern C your definition should look like
void matmul(size_t m, size_t n, float A[m][n]) {
...
}
where it is important that m and n come before A, such that they are already known, there.
Related
This is a theoretical question, there is no need to show code.
So, I would like to know how to return the result of a sum function, which will add two arrays using pointers.
First, should matrices be declared as a pointer right at the beginning?
So, in the function we will have
void sumMatrix (int ** m) (?)
From here, how to proceed to return the result of this sum, since the matrix itself cannot be returned
Options include:
Pass the function a pointer to where you want the result matrix stored.
Write code in the function to allocate space for the result matrix and return a pointer to that space.
Create a structure type to hold the result matrix and have the function return that structure by value.
Write the results into one of the input matrices.
It is impossible to pass arrays of any dimension to functions in C. It is impossible even to express the concept, because in most circumstances, including function-call expressions, values of array type are automatically converted to pointers. Thus,
Your function has no alternative but to receive its arguments in the form of pointers.
However, you should give some thought to the specific pointer types. C multidimensional arrays (e.g. int arr[3][4]) are structured as arrays of arrays, and the aforementioned automatic conversions yield pointers to arrays (int (*p)[4]), not pointers to pointers. On the other hand, you can construct arrays of pointers (int *arr[3]) and use the same syntax to access them as one does with multidimensional arrays. The automatic conversion of these to pointers does yield double pointers (int **p). Despite the matching access syntax, these alternatives very different in terms of memory layout and access efficiency.
It depends. Ignoring the for the moment the question of returning the sum, you have at least three good alternatives:
void sumMatrix(int r, int c, int **m1, int **m2); This is appropriate for array of pointers data layout. You an express the same thing as
void sumMatrix(int r, int c, int *m1[], int *m2[]);, and I would probably be inclined to do that myself.
void sumMatrix(int r, int c, int m1[r][c], int m2[r][c]); This is equivalent to
void sumMatrix(int r, int c, int m1[][c], int m2[][c]); and to
void sumMatrix(int r, int c, int (*m1)[c], int (*m2)[c]);
These rely on the variable-length array feature added to C in C99, and it is worth knowing that this feature became optional in C11. It assumes compact, efficient array-of-array data layout.
void sumMatrix(int r, int c, int *m1, int *m2); or, equivalently,
void sumMatrix(int r, int c, int m1[], int m2[]); This supposes the same array-of-array data layout as the previous, but requires you to perform the index calculations manually (x * c + y). It is useful if you want to have array-of-array layout with variable array dimensions, without depending on VLAs.
Personally, I would be inclined to choose array-of-arrays layout and one of the variations on the second signature option.
From here, how to proceed to return the result of this sum, since the matrix itself cannot be returned
You again have multiple options, but I would be inclined to add a fifth parameter, of the same type as the third and fourth, representing the result matrix. Because, again, it is necessarily a pointer, the data written into the pointed-to object by the function will be visible to the caller. The caller will then be responsible for passing a pointer to an existing object, which is convenient because it allows (but does not require) using an automatically allocated object.
Thus one complete possibility would be
void sumMatrix(int r, int c, int m1[r][c], int m2[r][c], int result[r][c]) {
// ...
}
which could be called like this:
int a[3][4], b[3][4], c[3][4];
// ... fill arrays a and b ...
summMatrix(3, 4, a, b, c);
// the result is in matrix c
Here is what is it written as rationale for adding the fancy * star syntax for declaring array types inside function prototypes - just for clarification before we get into the question:
A function prototype can have parameters that have variable length
array types (ยง6.7.5.2) using a special syntax as in
int minimum(int,int [*][*]); This is consistent with other C prototypes where the name
of the parameter need not be specified.
But I'm pretty confident that we can have the same effect by simply using only ordinary arrays with unspecified size like this (here re-writing the function example named minimum given above in the quote with what I believe exactly the same functionality (except for using size_t instead of int as first parameter which isn't that important in the case)):
#include <stdio.h>
int minimum(size_t, int (*)[]);
int (main)()
{
size_t sz;
scanf("%zu", &sz);
int vla[sz];
for(size_t i = 0; i < sz; ++i)
vla[i] = i;
minimum(sizeof(vla) / sizeof(*vla), &vla);
int a[] = { 5, 4, 3, 2, 1, 0 };
minimum(sizeof(a) / sizeof(*a), &a);
}
int minimum(size_t a, int (*b)[a])
{
for(size_t i = 0; i < sizeof(*b) / sizeof(**b); ++i)
printf("%d ", (*b)[i]);
return printf("\n");
}
Because I'm pretty sure that there was some place in the standard stating that 2 arrays are compatible only if their size are equal and no-matter if they are variable or not.
My point is also confirmed by the fact that the minimum definition wouldn't complain for "conflicting types" as it would if some of it's parameters had incompatible types (which I don't think is the case as both of those arrays have size which is unspecified at compile-time - I refer to the second parameter of minimum).
OK besides - can you point me 1 single use-case for [*] that can not be replaced using ordinary unspecified size arrays?
The above code compiles without any warnings using both clang and gcc. It also produces the expected output.
For anyone who doesn't know C (or anyone who thinks that he/she knows it) - function parameter of type array is implicitly transformed to "pointer to its elements type". So this:
int minimum(int,int [*][*]);
Gets adjusted to:
int minimum(int,int (*)[*]);
And then I'm arguing that it could be also written as:
int minimum(int,int (*)[]);
Without any consequences and with the same behavior as the 2 forms above. Thus making the [*] form obsolete.
OK besides - can you point me 1 single use-case for [*] that can not
be replaced using ordinary unspecified size arrays?
This would be the case, when you pass three-dimensional VLA array:
int minimum(size_t, int [*][*][*]);
This can be written as:
int minimum(size_t, int (*)[*][*]);
or even using an array of unspecified size:
int minimum(size_t, int (*)[][*]);
But you have no possibility to omit nor get around of the last indice, thus it has to stay as [*] in a such declaration.
[] can only be used as the leftmost "dimension specifier" of a multidimensional array, whereas [*] can be used anywhere.
In function parameter declarations, the leftmost (only!) [...] is adjusted to (*) anyway, so one could use (*) in that position at the expense of some clarity.
One can omit the dimension in the next-to-leftmost [...], leaving the empty brackets. This will leave the array element type incomplete. This is not a big deal, as one can complete it close to the point of use (e.g. in the function definition).
The next [...] needs a number or * inside which cannot be omitted. These declarations
int foo (int [*][*][*]);
int foo (int (*)[*][*]);
int foo (int (*)[ ][*]);
are all compatible, but there isn't one compatible with them that doesn't specify the third dimension as either * or a number. If the third dimension is indeed variable, * is the only option.
Thus, [*] is necessary at least for dimensions 3 and up.
I am reading C Programming: A Modern Approach by K.N.King to learn the C programing language and the current chapter tells about functions, and also array parameters. It is explained that one can use constructs like these to express the length of array parameters:
1.
void myfunc(int a, int b, int[a], int[b], int[*]); /* prototype */
void myfunc(int a, int b, int n[a], int m[b], int c[a+b+other_func()]) {
... /* body */
}
2.
void myfunc(int[static 5]); /* prototype */
void myfunc(int a[static 5]) {
... /* body */
}
So the question(s) are:
a. Are the constructs in example 1 purely cosmetic or do they have an effect on the compiler?
b. Is the static modifier in this context only of cosmetic nature? what exactly does it mean and do?
c. Is it also possible to declare an array parameter like this; and is it as cosmetic as example 1 is?
void myfunc(int[4]);
void myfunc(int a[4]) { ... }
The innermost dimension of function array parameters is always rewritten to a pointer, so the values that you give there don't have much importance, unfortunately. This changes for multidimensional arrays: starting from the second dimension these are then used by the compiler to compute things like A[i][j].
The static in that context means that a caller has to provide at least as many elements. Most compilers ignore the value itself. Some recent compilers deduce from it that a null pointer is not allowed as an argument and warn you accordingly, if possible.
Also observe that the prototype may have * so clearly the value isn't important there. In case of multidimensional arrays the concrete value is the one computed with the expression for the definition.
so I'm asked to make the following function:
int **multiplyM(int MA[][], int MB[][], int n, int m)
Which will multiply two matrices. The first one (MA) with dimensions n, n, and the second one (MB) with dimensions n, m. I have everything done with the program, but I get an error caused by the function itself, which says:
"array type has incomplete element type"
I know I can fix it by changing stuff in the function (like changing it to **MA and **MB), but the thing is, I'm not supposed to do that, because I'm supposed to make my program based on this function that was given to me.
So my question is: Is there a way to make this work WITHOUT changing the function?
The second dimension must be given for MA and MB
So,
#define SIZE_M 5 //Any constant
#define SIZE_N 6
int **multiplyM(int MA[][SIZE_M], int MB[][SIZE_N], int n, int m)
//Fix -> ^^^ ^^^
You cannot pass a multidimensional array to a function as you are doing. You need to specify the size of the the second dimension (and any further dimension) when declaring the function. Specifying the size here is important. If it were not mandatory, the compiler won't be able to deal with expression such Array[2][3]. The value used as array dimension must be a constant for ANSI C an other versions, but it can be a variable for C99 and successive versions. The C99 standard introduced the variable-length arrays feature, which allows to determine the size of an array at run-time.
So:
#define N 10
#define M 5
int **multiplyM(int MA[][N], int MB[][M], int n, int m)
I know I can fix it by changing stuff in the function (like changing
it to **MA and **MB), but the thing is, I'm not supposed to do that,
because I'm supposed to make my program based on this function that
was given to me.
Without modifying at least the declaration of the function, you are not going to solve this problem.
This is the declaration of the function and the compiler gives: "error: array type has incomplete element type" in both the declaration and definition. I don't see the error.
void calculate(float matrM[][], int origMatr[][]);
C doesn't have very powerful arrays, especially not when calling functions. Basically, the only data that gets sent to the function at run-time is the address of the first element. This means that the dimensions must be known in all directions except the topmost, in order for the compiler to be able to generate indexing code. Thus, you can't have two unknown dimensions, that makes indexing impossible and thus isn't allowed.
You can't declare an "any size" array like that, you must specify all dimensions except one, otherwise there's no way to compute the address of a random element in the array.
It's often better to break it down as:
void calculate(float *out, const float *in, size_t width, size_t height);
which will let you treat any memory block as float arrays, but requires you to specify the size, and the write the indexing manually:
out[y * width + x] = in[y * width + x];
Your "inner" dimensions must have a size, e.g.:
void calculate(float matrM[][5], int origMatr[][7]);
Otherwise, the compiler wouldn't know how to generate indexing code to access a particular element. Note also that these sizes must be compile-time constant expressions.
For more details, see How do I write functions which accept two-dimensional arrays when the width is not known at compile time? from the C FAQ.
If you have a modern C compiler (C99 would do) you can have the "extra" dimensions as expressions of other function parameters that precede
void calculate(size_t n, float matrM[][n], int origMatr[][n]);
you'd just have to be careful that you'd have equivalent parameters in your declaration (the one I gave) and the definition (the one that provides the implementation of the function).
You only get one "free" dimension (the first one) when you're passing an array to a function. The correct declaration will look more like:
void calculate(float matrM[][N], int origMatr[][M]);