Return Matrix on C

Here is my code below.

#include <stdio.h> #define _USE_MATH_DEFINES #include <math.h> void rot(int angle); static double R[3][3]={0}; int main(void) { int angle = 30; rot(angle); int i, j = 0; for (i = 0; i < 3;i++) { for (j = 0; j < 3; j++) { printf("%lf\n", R[i][j]); } } return 0; } void rot(int angle) { double cang = cos(angle*M_PI / 180); double sang = sin(angle*M_PI / 180); R[0][0] = cang; R[1][1] = cang; R[1][0] = -sang; R[0][1] = sang; } 

Now the rot function does not return any value. But since R is static double, I was able to print R modified by the rot function. I want to change the rot function to return R [3] [3] (two-dimensional array). So I want to use the rot function on the main, for example

 double R1=rot(30); double R2=rot(60); 

Is there any way to make this possible?

+5
source share
2 answers

C does not allow returning arrays from functions. An array can be allocated in the calling function, or the array can be dynamically allocated in the function and a pointer to the returned array. Note that the array that is defined in the function is local, and returning a pointer to such an array is useless, since local variables cease to exist after the function returns. Dynamic distributions, on the other hand, continue to exist. The third option is to use the fact that although arrays cannot be returned from functions in C, a struct can be returned from functions and assigned.

Define an array in the caller

A simple solution is to define an array in the calling function. This array will be modified in the rot_3x3_a() function. Note that instead of relying on a user who is mindful of null array initialization, memset() (defined in string.h ) is used inside the function for a null array.

Using this method has the advantage of avoiding dynamic allocation and the need to free it later to avoid memory leaks. All methods require that the variable be declared in the calling function that receives the rotation matrix, but this method also requires that the array be passed to the function, so this may not meet the OP requirements.

Dynamic allocation

A more attractive solution is to use dynamic allocation. The rot_3x3_b() function accepts only the int argument, as the OP example suggests, but returns a pointer to an array of 3 double (this is a pointer type, which in most expressions decomposes into a 3x3 array). typedef used here, so Matrix_3x3 is a pointer to an array of 3 double s; this makes it easy to write a function prototype.

Here memset() could be used with malloc() , but calloc() was used instead, which automatically initializes the selection to zero. Note that in the function rot_3x3_b() , if the distribution is not executed, nothing happens and a null pointer is returned. The caller must verify this and handle the error accordingly.

The advantages of using this approach are that the resulting matrix distribution can simply be indexed as a 2d array, and the arguments to the function meet OP requirements. The disadvantages are that it is somewhat more complicated for the code than other solutions, and more prone to errors, and the user of this function must remember the free allocated memory in order to avoid memory leaks.

Array flow in structure

As long as C does not allow you to assign an array to another array, a struct can be assigned to another struct . In addition, a struct is an l value that can be returned from a function. So, one of the solutions to the problem is to create a struct containing a 3x3 array in the rot_3x3_c() function. The element of the struct array can be populated before returning the struct function from the function. In this case, the designated initializer was used to zero initialize the element of the struct array.

The advantage here is ease of implementation. There is no dynamic allocation, so no need to worry about memory leaks. The disadvantage is that the array must be accessible as a member of struct , so the user of this function must declare struct Mtrx_3x3 R_c to get the value returned by the function, and must remember to access the array using R_c.mx .

Regarding the M_PI constant

The C standard not only does not define this constant, but provides that the corresponding implementation should not define it by default. This means that if a particular implementation defines an M_PI , it is an extension that must be explicitly included. As a general extension, this constant is defined. For example, if compiled with gcc -std=gnu11 , there is no need to explicitly enable M_PI . But if one of the more strict parameters is used, for example, gcc -std=c11 , M_PI must be explicitly included. Here is a question that discusses the problem .

Now _USE_MATH_DEFINES works with Microsoft implementations, but does not work with GCC on Linux (as far as I know). For greater portability, note that M_PI is defined in POSIX . This can be activated using the _XOPEN_SOURCE 700 function test macro. Or add as the very first line in the source file:

 #define _XOPEN_SOURCE 700 

or enable from the command line when compiling with:

 gcc -std=c11 -D_XOPEN_SOURCE=700 

Please note that when using #define to enable functions, it must be at the very beginning of the source file. Of course, here you can use -std=c99 or -std=c89 instead of std=c11 .

This will work on Linux systems that closely match POSIX, but I'm not sure if it will work on Microsoft systems, which follows POSIX much less closely. For maximum portability, it's best to simply define the constant explicitly using:

 #define M_PI 3.14159265358979323846 

Program example

Here is a program that illustrates each of the above approaches:

 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #define M_PI 3.14159265358979323846 typedef double (*Matrix_3x3)[3]; struct Mtrx_3x3 { double mx[3][3]; }; void rot_3x3_a(int angle, Matrix_3x3 mtrx); Matrix_3x3 rot_3x3_b(int angle); struct Mtrx_3x3 rot_3x3_c(int angle); void print_3x3_matrix(Matrix_3x3); int main(void) { int angle = 30; /* Allocate for matrix in caller */ puts("Method a: pass an array"); double R_a[3][3]; rot_3x3_a(angle, R_a); print_3x3_matrix(R_a); putchar('\n'); /* Use dynamic allocation in function; must remember to free()! */ puts("Method b: use dynamic allocation"); Matrix_3x3 R_b = rot_3x3_b(angle); if (R_b == NULL) { perror("Allocation failure in rot_3x3_b()"); } else { print_3x3_matrix(R_b); putchar('\n'); } /* Return the array inside a struct from the function */ puts("Method c: wrap the array in a struct"); struct Mtrx_3x3 R_c = rot_3x3_c(angle); print_3x3_matrix(R_c.mx); // remember the array is R_c.mx putchar('\n'); /* Cleanup */ free(R_b); return 0; } /* Takes an array as an argument */ void rot_3x3_a(int angle, Matrix_3x3 mtrx) { /* Zero the array first */ memset(mtrx, 0, sizeof *mtrx * 3); double cang = cos(angle * M_PI / 180); double sang = sin(angle * M_PI / 180); mtrx[0][0] = cang; mtrx[1][1] = cang; mtrx[1][0] = -sang; mtrx[0][1] = sang; } /* Returns a pointer to a dynamically allocated array which must be * deallocated by the caller with free(), or returns NULL */ Matrix_3x3 rot_3x3_b(int angle) { Matrix_3x3 mtrx = calloc(3, sizeof *mtrx); if (mtrx) { double cang = cos(angle * M_PI / 180); double sang = sin(angle * M_PI / 180); mtrx[0][0] = cang; mtrx[1][1] = cang; mtrx[1][0] = -sang; mtrx[0][1] = sang; } return mtrx; } /* Returns a Mtrx_3x3 struct with a 3x3 array in the mx field */ struct Mtrx_3x3 rot_3x3_c(int angle) { struct Mtrx_3x3 mtrx = { .mx = {{ 0 }} }; double cang = cos(angle * M_PI / 180); double sang = sin(angle * M_PI / 180); mtrx.mx[0][0] = cang; mtrx.mx[1][1] = cang; mtrx.mx[1][0] = -sang; mtrx.mx[0][1] = sang; return mtrx; } void print_3x3_matrix(Matrix_3x3 mtrx) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { printf("%10.5f", mtrx[i][j]); } putchar('\n'); } } 

Program output:

 Method a: pass an array 0.86603 0.50000 0.00000 -0.50000 0.86603 0.00000 0.00000 0.00000 0.00000 Method b: use dynamic allocation 0.86603 0.50000 0.00000 -0.50000 0.86603 0.00000 0.00000 0.00000 0.00000 Method c: wrap the array in a struct 0.86603 0.50000 0.00000 -0.50000 0.86603 0.00000 0.00000 0.00000 0.00000 
+3
source

A duplicate is not necessarily good for this. Since these R² matrices should be easy to copy, the best way to represent them is to put them in a structure:

 typedef struct Matrix3x3 { double v[3][3]; } Matrix3x3; 

Then it is trivial to get them as return values:

 Matrix3x3 rot(int angle) { Matrix3x3 m = {0}; double cang = cos(angle*M_PI / 180); double sang = sin(angle*M_PI / 180); mv[0][0] = cang; mv[1][1] = cang; mv[1][0] = -sang; mv[0][1] = sang; return m; } 

Unlike arrays, the structure containing the array is also easy to copy:

 Matrix3x3 x = y; 
+4
source

Source: https://habr.com/ru/post/1271868/


All Articles