Passing [CONST][] array to C function? - c

I am designing a C function interface which involves a 2d array.
The problem is, row dimension is constant, and column one should be user defined.
#define ROWS (65)
void init(int columns, float array[ROWS?][columns?])
{
...
}
void main()
{
float array1[ROWS][30];
float array2[ROWS][50];
init(30, array1);
init(50, array2);
}
How do I design an interface to be able to pass this kind of array down to function?
p.s.
Can't do it the other way around, where columns would be constant, because must use some 3rd pary libraries that want it this way.

Simply do as you wrote in your example
void init (size_t rows, size_t columns, float array[rows][columns])
Then you can pass compile-time constants or run-time variables to the function as you please. You'll also need to have C compiler from the current millennium (C99 or later).

You mentioned c99 in a comment. So it shouldn't be too difficult to approximate what you want. In fact, you are almost there yourself. It can look like this:
#define ROWS 65
void init(int columns, float array[static ROWS][columns])
{
}
Now array is of a variably modified type, and columns is user defined. static ROWS means callers must pass in an array of at least 65 rows, or undefined behavior will ensue. That's pretty much as close to forcing them to pass 65 as you can.

Related

Array lengths in array parameters

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.

Can a two-dimensional array in C be initialized without explicit size?

I have a question regarding two-dimensional arrays in C. I know now (from direct compiler experience) that I can't initialize such an array analogously to one-dimensional arrays like this:
int multi_array[][] = {
{1,2,3,4,5},
{10,20,30,40,50},
{100,200,300,400,500}
};
> compiler output:
gcc -o arrays arrays.c
arrays.c: In function ‘main’:
arrays.c:8:9: error: array type has incomplete element type
The closest solution that works is to provide the number of columns explicitly like this:
int multi_array[][5] = {
{1,2,3,4,5},
{10,20,30,40,50},
{100,200,300,400,500}
};
My question is: can it be done neatly without supplying the number explicitly (which after all the compiler should be able to infer itself)? I'm not talking about manually constructing it with malloc or something but rather something close to what I tried.
Also, can someone knowledgeable about C compilers explain from a low-level perspective why my initial attempt does not work?
I used plain gcc with no non-standard options to compile the code.
Thanks
2D arrays in C are stored in contiguous memory locations. So if you do not provide the number of rows or the number of columns, how will the compiler know how many rows and column there are?
For a row major matrix, rows contents are at contiguous memory positions. So you need to specify at least the number of columns. Similarly for a column major matrix, you need to specify at least the number of rows. Whether it is row major or column major is defined by architecture. It seems that what you have is a row major architecture.
You can do this using the C99 compound literal feature.
A partial idea is that the length of an initializer list can be determined like this:
sizeof (int[]){ 1, 2, 3, 4, 5 } / sizeof(int)
We need a workaround for the fact that the only way you can pass an argument containing a comma to a macro is to put parentheses around (part of) the argument:
#define ROW(...) { __VA_ARGS__ }
Then the following macro deduces the second dimension from the first row:
#define MAGIC_2DARRAY(type, ident, row1, ...) \
type ident[][sizeof (type[])row1 / sizeof (type)] = { \
row1, __VA_ARGS__ \
}
It only works if there are at least two rows.
Example:
MAGIC_2DARRAY(int, arr, ROW(7, 8, 9), ROW(4, 5, 6));
You probably do not want to use this in a real program, but it is possible.
For passing this kind of array to functions, the C99 variable length array feature is useful, with a function like:
void printarr(int rows, int columns, int array[rows][columns]) { ... }
called as:
printarr(sizeof arr / sizeof arr[0], sizeof arr[0] / sizeof arr[0][0], arr);
Not a direct answer to those questions in the original post, I just want to point out that what the asker propose may be not such a good or useful idea.
The compiler indeed can infer from
int multi_array[][] = {
{1,2,3,4,5},
{10,20,30,40,50},
{100,200,300,400,500}
};
the structure of multi_array.
But when you want to declare and define a function (this declaration and definition could be in another compilation unit or source file) that supposes to accept multi_array as one of its argument, you still need to do something like
int foo(..., int multi_array[][COL], ...) { }
Compiler needs this COL to do proper pointer arithmetic in foo().
Usually, we define COL as a macro that will be replaced by an integer in a header file, and use it in the definitions of multi_array and foo():
int multi_array[][COL] = { ... };
int foo(..., int multi_array[][COL], ...) { }
By doing this, it is easy to make sure they are the same. And let compiler to infer the structure of multi_array according to its initialization, when you give it a wrong initialization, you actually introduce a bug in your code.
No you can't do it. If you even don't initialize, you can't define an int array[][];
Create a structure with 1d arrays. However, if you follow this method you can create new arrays but it will be a function call to change sizes and values. A dynamic matrix approach could come close to solving your issue.

(C) Function with bidimensional array

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.

"Error: array type has incomplete element type" on function declaration

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

Not able to understand this function definition

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.

Resources