Matrix multiplication in C with unknown sizes - c

As a C newbie I am struggeling with matrix multiplication in C. The problem is, that the multiplication should be flexible and the rows, cols are not known before.
The dimensions, matrices and results for different matrix multiplications are all defined in an header file however I would like to have an matrix multiplication function that works for all of them.
Up to now I have:
void matrix_multiply(int rows1, int cols1, int cols2, float matrix1[rows1][cols1], const float matrix2[cols1][cols2], float result[rows1][cols2])
{
for (int i = 0; i < rows1; i++)
{
for (int j = 0; j < cols2; j++)
{
result[i][j] = 0;
for (int k = 0; k < cols1; k++)
{
result[i][j] += matrix1[i][k] * matrix2[k][j];
}
}
}
}
My local compiler accepts that. However when I try with other compilers at godbolt the compiler may return an error. It seems to compile with gcc and clang however with others I get errors:
x86 msvc v19.27:
<source>(2): error C2057: expected constant expression
<source>(2): error C2466: cannot allocate an array of constant size 0
<source>(2): error C2087: 'matrix1': missing subscript
<source>(2): error C2087: 'matrix2': missing subscript
<source>(2): error C2087: 'result': missing subscript
Is there a way to programm a matrix multiplication function that works for every compiler?

I don't know why your code works, it should fail on every C compiler.
There can be at most one variable dimension size.
You have two option:
Change 2d-array into array of arrays:
void matrix_multiply(
int rows1, int cols1, int cols2,
const float *matrix1[],
const float *matrix2[],
float *result[])
Or, just use 1-dimensional row-major array. For example:
// 2-dimensional array
int x[ROWS][COLS];
// can be replaced by
int y[ROWS*COLS];
// where element
x[r][c];
// corresponds to
y[r*COLS+c];

Related

Multi dimensional array as VLA function parameter with explicit size

Context
I am working on a computational fluid dynamics code where I work with vector and matrices. Consequently I want to pass them to functions every once in a while.
The memory for vectors and matrices is allocated as follows consequently throughout the code
double* vec = calloc(n, sizeof(double));
double* mat = calloc(n * m, sizeof(double));
meaning I would like to access matrices like this mat[i * m + j].
In order to "auto-document" functions I would like to include the size of these arrays into the function prototype. For vectors this works flawlessly and looks something like this
// vecmat.h
void vec_print(size_t n, double const vec[restrict n]);
// vecmat.c
void vec_print(size_t n, double const vec[restrict n])
{
for (size_t i = 0; i < n; ++i) {
printf(DBL_FMT, vec[i]);
}
putchar('\n');
}
Problem
To keep things consistent, I would like to do the same thing with matrices, so I did this
// vecmat.h
void mat_print(size_t n, size_t m, double const mat[restrict n * m]);
// vecmat.c
void mat_print(size_t n, size_t m, double const mat[restrict n * m])
{
for (size_t i = 0; i < n; ++i) {
for (size_t j = 0; j < m; ++j) {
printf(DBL_FMT, mat[i * m + j]);
}
putchar('\n');
}
putchar('\n');
}
Here comes the issue though. When I compile, I get the following warning
src/vecmat.c:21:49: warning: argument 3 of type ‘const double[restrict n * m]’ declared with mismatched bound ‘n * m’ [-Wvla-parameter]
21 | void mat_print(size_t n, size_t m, double const mat[restrict n * m])
| ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
In file included from src/vecmat.c:1:
src/vecmat.h:16:49: note: previously declared as ‘const double[restrict n * m]’ with bound ‘n * m’
16 | void mat_print(size_t n, size_t m, double const mat[restrict n * m]);
| ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
The code compiles fine and also produces the expected output, but I would like to get rid of the warning message. I know that C does not use the provided dimension n * m of the array, but rather silently converts the argument into double* restrict mat. The problem seems to have something to do with the fact that I am using a multiplication and gcc somehow thinks that the array bounds are inconsistent.
Question
Can I somehow modify my syntax to avoid this compiler warning, or can I somehow disable this warning in gcc altogether? (gcc does not have the flag -Wno-vla-parameter)
EDIT -Wno-vla-parameter does exist and removes the warnings
Solution
Matrices should be allocated as follows:
double (*mat)[m] = calloc(n * sizeof(*mat));
The function calls with matrices in them should be altered as follows:
void mat_print(size_t n, size_t m, double const mat[restrict n][m]);
This makes it possible to access the matrix elements in a simpler form mat[i][j].
Unfortunately gcc still complains, because the function uses const. (https://godbolt.org/z/6xh5Exsff) If this also has a clever fix my initial and all subsequent problems would be solved :D
Additional resources:
http://c-faq.com/aryptr/dynmuldimary.html
http://c-faq.com/aryptr/ary2dfunc3.html
Additional Info
gcc version 11.1.0
-std=c99

Can't modify a 2D array of an unknown size within a fucntion in C

I new to C, and I just can't figure out how to modify a 2D array in a function.
Here's the code I tried:
void init_matrix(int **M) {
M[0][0] = 1;
}
int main(void) {
int M[3][3];
init_matrix(M, 3);
return 0;
}
(Please note that this code is voluntarily stripped down in order to focus on the issue, I need my function to be able to work on arrays of a globally unknown size (though it could be a parameter of the function))
When I try to run this, it just gets stuck... The debugger says it's a problem of right to write in this memory slot.
How would you write the init_matrix function in the C spirit ?
Why can't I write in my matrix ?
I would like to use as few "advanced" concepts and function as possible.
Thanks in advance =)
An array is not a pointer. You need to give the dimensions of the array when you pass it as a function parameter.
void init_matrix(size_t x, size_t y, int matrix[x][y])
{
for (size_t i = 0 ; i < x ; ++i)
{
for (size_t j = 0 ; j < y ; ++j)
matrix[i][j] = 1;
}
}
int main(void)
{
int matrix[5][3];
init_matrix(5, 3, matrix);
return (0);
}
The function init_matrix() takes as parameters the dimensions, then the array (this order is important here). The "double loop" is a classic for running through a "2D memory area" like our array.
(Note that you can forget the first dimension,
void init_matrix(size_t x, size_t y, int matrix[][y])
also works)

C: Passing an arbitrary sized array to a function

I am attempting to write a C program which reads input from a text file and puts it into a 2D-array where Rows are lines and columns are characters.
However I am confused after reading the following article:
http://c-faq.com/aryptr/pass2dary.html
The function definition which I am thinking of is
int processArray(char **text) {
...
...
}
where I am attempting to pass in a pointer to a 2D array whose dimensions I don't know until runtime. I would like to be able to access the elements using two square brackets [] []
However in the link it says the following:
An intermediate pointer would have to be used when attempting to call
it with a two-dimensional array:
extern g(int **ipp);
int *ip = &array[0][0];
g(&ip); /* PROBABLY WRONG */
but this usage
is misleading and almost certainly incorrect, since the array has been
``flattened'' (its shape has been lost).
What is wrong with the above declaration?
How should you define an array to multiple dimensions?
Leaving aside VLAs you can use a simple pointer to point your matrix:
#include "stdio.h"
#define MAX_ROWS 2
#define MAX_COLS 2
void test (int *matrix, int col_max, int row_max)
{
for (int i=0; i<row_max; i++)
{
for (int j=0; j<col_max; j++)
{
printf("matrix[%d][%d]= %d\n", i, j, *(matrix+(i*col_max)+j) );
}
}
}
int main(void)
{
int matrix[MAX_ROWS][MAX_COLS] = { {1,2}, {3,4} };
test(&matrix[0][0], MAX_ROWS, MAX_COLS);
return 0;
}

How can I define typedef matrix in ansi c

I have 2 .c files, adj.c and main.c.
What i try to do is to define at adj.c
#define N 12
typedef int adj_mat[N]
and use this typedef at main.c as two dimensional array
I tried to define like this,
adj_mat mat[N];
I have a fucntion in my program that fills this matrix with zero, with definition:
void fill_mat_zero(adj_mat);
My problem that if i run this program with Visual studio there are no problems,
but if i try to compile it in Ubuntu it shows me an error that looks like:
note: expected ‘int ’ but argument is of type ‘int ()[12]’
What is the problem here? The definiton of typedef is not correct?
Is there a way to define an adn_mat[N] array as typedef and later use it as multidimensional array?
thank you.
It is unclear from the question whether you want a 1-D array which you will index as if it is a 2-D, for example
adj_mat[3*row + col]
In this case, your declararations are only partially made. This compiles cleanly, I removed the [N] from your declaration and added an identifier to the function argument.
#define N 12
typedef int adj_mat[N];
void fill_mat_zero(adj_mat m) {
int i;
for (i=0; i<N; i++)
m[i] = 0;
}
int main(void){
adj_mat mat;
fill_mat_zero(mat);
return 0;
}
Or perhaps you want to make it 2-D by having an array of adj_mat[].
#define N 12
typedef int adj_mat[N];
void fill_mat_zero(adj_mat *m, int size) {
int i, j;
for (i=0; i<N; i++)
for (j=0; j<size; j++)
m[i][j] = 0;
}
int main(void){
adj_mat mat[N];
fill_mat_zero(mat, N);
return 0;
}
But if you want to fill an array with 0 it is more efficient to use memset().
This
#define N 12
typedef int adj_mat[N]
defines adj_mat as 12-element array of type int.
This
adj_mat mat[N];
defines mat as a 12-element array of type adj_mat, which means mat is actually an int[12][12]. So you get the error.
And
void fill_mat_zero(adj_mat);
is a declaration, not a definition. A definition has a body of code. Think of a declaration as just like a customs declaration when you're entering a country - you declare that you have something, but the declaration isn't actually that thing.

Malloc compile error: a value of type "int" cannot be used to initialize an entity of type int (*)[30]

I must have tried 20 ways of doing this by now. I really need help, no matter what I do i get a error similar to this one.
a value of type "int" cannot be used to initialize an entity of type "int (*)[30]"
i.e. this will get me such an error
int(*array)[160] = malloc((sizeof *array) * 10);
and doing something like this
int** Make2DintArray(int arraySizeX, int arraySizeY) {
int** theArray;
theArray = (int**) malloc(arraySizeX*sizeof(int*));
int i;
for (i = 0; i < arraySizeX; i++)
{
theArray[i] = (int*) malloc(arraySizeY*sizeof(int));
}
return theArray;
}
will get me this
"void *(size_t)" in "memory.c" at line 239 and: "int()"
does anyone have a solution for how to successful allocate a 2dArray of int[160][10]
Try this:
int **array;
array = malloc(rows * sizeof(int *));
for (i = 0; i < rows; i++)
array[i] = malloc(cols * sizeof(int));
// Some testing
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++)
array[i][j] = 0; // or whatever you want
}
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++)
printf("%d ", array[i][j]);
}
In your case rows = 160 and cols = 10. Is one possible solution.
With this approach you can use the two indexes:
Both of these compile fine for me. The first error is common when you forget to #include <stdlib.h> prior to using functions declared within said-same (such as malloc(size_t)), which I did not forget to do.
C has some interesting compile-time behaviors, among them the ability to invoke a function that has never been seen before (neither prototype definition nor implementation). Upon encountering such a call, C assumes the function is:
Something that returns int
Takes an unknown number of arguments, so the caller can pass whatever it wants (including the wrong things).
Eg., the function is implicitly assumed to be of the form:
int func();
Often you won't even notice, save for warnings from your compiler that report something to the effect of:
Warning: implicit declaration of `func` assumed to return `int`
and if you're on-the-ball, you have your warning levels turned up with warnings-as-errors enabled and will catch this.
But what if you don't? And what if the "thing" returned from the function cannot be content-represented by the data size in an implementation int ? What if, for example, int were 32-bit, but data pointers were 64-bit? For example, lets assume char *get_str() is declared in some header file you're not including, and implemented in a .c file you compile and link with your program, that looks like this:
#include <stdio.h>
// Note: NO prototype for get_str
int main()
{
char *s = get_str();
printf("String: %s\n", s);
return 0;
}
Well, the compiler should puke, telling you that int and char* are not compatible (shortly after it warns you get_str is assumed to return int). But what if you force the compiler's hand by telling it to make a char* one way or another:
#include <stdio.h>
// Note: NO prototype for get_str
int main()
{
char *s = (char*)get_str(); // NOTE: added cast
printf("String: %s\n", s);
return 0;
}
Now, without warnings-as-errors enabled, you'll get a implicit declaration warning, and thats it. The code will compile. But will it run ? If sizeof(int) != sizeof(char*), (32-bit vs 64-bit) likely not. The value returned from get_str is a 64-bit pointer, but the caller is assuming only 32-bits is returned, then forcing it to a 64-bit pointer. In short, the cast has hidden the error and opened pandora's box of undefined behavior.
So how does all of this relate to your code? By not including <stdlib.h> the compiler doesn't know what malloc is. So it assumes it is of the form:
int malloc();
Then, by casting the result to (int**) you're telling the compiler "whatever comes out of this, make it a int**". At link time, _malloc is found (no parameter signature via name mangling like C++), wired up, and your program is ready to party. But on your platform int and data pointers are not the same size, thus you end up with several undesirable consequences:
The cast hides the real error.
A bogus pointer is manufactured from half the bits of the real returned pointer.
As a cruel dose of salt to the wound, the allocated memory is leaked, as there are no valid pointers anywhere that reference it (you just destroyed the only one by only keeping half of it).
Probably the most undesirable, the code will exhibit normal behavior if compiled on an implementation where sizeof(int) == sizeof(int**).
So you build this on your 32-bit Debian box, all looks well. You turn in your homework to the professor who builds it on his 64bit Mac and it crashes, you fail the assignment, fail the class, drop out of college, and spend the next ten years petting the cat while watching Seinfeld reruns in your mom's basement wonder what went wrong. Ouch.
Don't treat casting like some silver bullet. It isn't. In C, it is needed far less often than people use it, and if used in the wrong place, can hide catastrophic errors. If you find a point in your code where something won't compile without a hard cast, look again. Unless you're absolutely, positively sure the cast is the right thing to do, odds are its wrong.
In this case it hid the real error, that you neglected to give enough info to your compiler to know what malloc really does.
To allocate the array:
int *array = malloc(sizeof(int) * 160 * 10);
Then use code like:
array[10 * row + column] = value;
(Where row goes from 0 to 159 inclusive and column goes from 0 to 9 inclusive.)
I have a note for rendon's answer:
For his code, Visual C++ says for every "=" operations: error C2440: '=' : cannot convert from 'void *' to 'int **'
By making some changes, it works for me, but I'm not sure that it does the same, so I afraid of editing his code. Instead, here's me code, that seems to work for a first impression.
int **a;
a = (int **)malloc(rows * sizeof(int));
for (i = 0; i < rows; i++)
{
a[i] = (int *)malloc(cols * sizeof(int));
}
for (j=0;j<rows;j++)
{
for (i=0;i<cols;i++)
{
a[i][j] = 2;
}
}
Actually, I did it with a custom struct instead of ints but I think either way should it work.
Don't mind me I'm just adding an example using calloc
void allocate_fudging_array(int R, int C)
{
int **fudging_array = (int **) calloc(R, sizeof(int *));
for(int k = 0; k < R; k++)
{
fudging_array[k] = (int*) calloc(C, sizeof(int));
}
}
// a helper function to print the array
void print2darr(int **arr, int R, int C)
{
for(int i = 0; i < R; i++)
{
for(int j = 0; j < C; j++)
{
printf(" %d ", arr[i][j]);
}
printf("\n");
}
}
2D array to store char *
char ***array;
int rows = 2, cols = 2, i, j;
array = malloc(sizeof(char **)*rows);
for (i = 0; i < rows; i++)
array[i] = malloc(cols * sizeof(char *));
array[0][0] = "asd";
array[0][1] = "qwe";
array[1][0] = "stack";
array[1][1] = "overflow";
for(i=0;i<rows;i++){
for(j=0;j<cols;j++){
printf("%s ",array[i][j]);
}
printf("\n");
}

Resources