Reputation: 55
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;
}
For now, function rot do not return any value itself. But since R is static double, I was able to print out the R changed by rot function. I want to change function rot to return R[3][3](two dimension array). So I want to use rot function on main like
double R1=rot(30);
double R2=rot(60);
is there any way to make it possible?
Upvotes: 5
Views: 1846
Reputation: 133899
The duplicate isn't necessarily good for this. As these R² matrices need to be easily copyable, the best way to represent them is to put them in a struct:
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);
m.v[0][0] = cang;
m.v[1][1] = cang;
m.v[1][0] = -sang;
m.v[0][1] = sang;
return m;
}
Unlike arrays, a struct that contains an array is easy to copy too:
Matrix3x3 x = y;
Upvotes: 4
Reputation: 21318
C does not allow arrays to be returned from functions. An array can be allocated in the calling function, or an array can be dynamically allocated in the function and a pointer to the array returned. Note that an array which is defined in a function is local, and returning a pointer to such an array is useless since local variables cease to exist after the function has returned. Dynamic allocations, on the other hand, continue to exist. A third option is to use the fact that, while arrays can't be returned from functions in C, struct
s can be returned from functions and assigned.
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 the user of the function remembering to zero-initialize the array, memset()
(defined in string.h
) is used within the function to zero the array.
Using this method has the advantage of avoiding dynamic allocation, and the need to deallocate later to avoid memory leaks. All of the methods require a variable to be declared in the calling function which will receive the rotation matrix, but this method also requires that the array be passed to the function, so this may not meet OP requirements.
A more involved solution is to use dynamic allocation. The rot_3x3_b()
function takes only an int
argument, as OP example suggests, but returns a pointer to an array of 3 double
s (this is the type of pointer that a 3x3 array decays to in most expressions). A typedef
has been used here, so Matrix_3x3
is a pointer to an array of 3 double
s; this makes it easier to write the function prototype.
Here, memset()
could have been used with malloc()
, but instead calloc()
has been used, which automatically zero-initializes the allocation. Note that, in the rot_3x3_b()
function, if the allocation fails nothing else happens, and a null pointer is returned. The caller must check for this and handle the error appropriately.
The advantages of using this approach are that the resulting matrix allocation can simply be indexed as a 2d array, and arguments to the function match OP requirements. The disadvantages are that this is slightly more complex to code than the other solutions, and more error-prone, and the user of this function must remember to free
the allocated memory to avoid memory leaks.
While C does not allow an array to be assigned to another array, a struct
can be assigned to another struct
. Further, a struct
is an lvalue which can be returned from a function. So, one solution to the problem is to create a struct
containing a 3x3 array within the rot_3x3_c()
function. The array member of the struct
can be populated before returning the struct
from the function. In this case, a designated initializer has been used to zero-initialize the array member of the struct
.
The advantage here is simplicity of implementation. There is no dynamic allocation, so no need to worry about memory leaks. The disadvantage is that the array must be accessed as a struct
member, so the user of this function must declare struct Mtrx_3x3 R_c
to receive the value returned from the function, and must remember to access the array with R_c.mx
.
M_PI
The C Standard not only does not define this constant, but stipulates that a conforming implementation must not define it by default. This means that if a particular implementation does define M_PI
, it is an extension which must be explicitly enabled. As a common extension, this constant is defined. For example, if compiling with gcc -std=gnu11
, there is no need to explicitly enable M_PI
. But if one of the stricter options is used, e.g., gcc -std=c11
, M_PI
must be explicitly enabled. Here is an SO question that discusses the issue.
Now, _USE_MATH_DEFINES
works with Microsoft implementations, but does not work with GCC in Linux (as far as I am aware). For more portability, note that M_PI
is defined in POSIX. This can be enabled by using the feature test macro _XOPEN_SOURCE 700
. Either 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
Note that, when using a #define
to enable features, it must be at the very beginning of the source file to work. Of course, -std=c99
or -std=c89
could also be used here instead of std=c11
.
This will work on Linux systems, which closely conform to POSIX, but I am not sure that it will work on Microsoft systems, which follows POSIX much less closely. For maximum portability, it is best to simply define the constant explicitly, using:
#define M_PI 3.14159265358979323846
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
Upvotes: 3