C 不允许从函数返回数组。可以在调用函数中分配数组,也可以在函数中动态分配数组并返回指向该数组的指针。请注意,在函数中定义的数组是局部的,返回指向此类数组的指针是没有用的,因为在函数返回后局部变量不再存在。另一方面,动态分配继续存在。第三种选择是利用这样一个事实,虽然数组不能从 C 中的函数返回,structs 可以从函数返回并赋值。
在调用者中定义一个数组
一个简单的解决方案是在调用函数中定义一个数组。该数组将在rot_3x3_a() 函数中进行修改。请注意,在函数中使用memset()(在string.h 中定义)将数组归零,而不是依赖函数的用户记住对数组进行零初始化。
使用这种方法的优点是可以避免动态分配,并且需要稍后释放以避免内存泄漏。所有的方法都需要在调用函数中声明一个变量来接收旋转矩阵,但是这种方法也需要将数组传递给函数,所以可能不符合OP的要求。
动态分配
一个更复杂的解决方案是使用动态分配。 rot_3x3_b() 函数只接受一个 int 参数,正如 OP 示例所暗示的那样,但返回一个指向 3 个 doubles 数组的指针(这是 3x3 数组在大多数表达式中衰减到的指针类型)。这里使用了typedef,所以Matrix_3x3是一个指向3个doubles的数组的指针;这样更容易编写函数原型。
这里,memset() 可以与malloc() 一起使用,但使用了calloc(),这会自动将分配初始化为零。请注意,在rot_3x3_b() 函数中,如果分配失败,则不会发生其他任何事情,并返回一个空指针。调用者必须对此进行检查并适当地处理错误。
使用这种方法的优点是生成的矩阵分配可以简单地索引为二维数组,并且函数的参数符合 OP 要求。缺点是这个代码比其他解决方案稍微复杂一些,而且更容易出错,而且这个函数的用户必须记住free分配的内存以避免内存泄漏。
将数组包装在结构中
虽然 C 不允许将一个数组分配给另一个数组,但可以将一个 struct 分配给另一个 struct。此外,struct 是一个可以从函数返回的左值。因此,该问题的一种解决方案是在 rot_3x3_c() 函数中创建一个包含 3x3 数组的 struct。在从函数返回struct 之前,可以填充struct 的数组成员。在这种情况下,已使用指定的初始化程序对 struct 的数组成员进行零初始化。
这里的优点是实现简单。没有动态分配,所以不用担心内存泄漏。缺点是数组必须作为struct成员访问,所以这个函数的用户必须声明struct Mtrx_3x3 R_c才能接收函数返回的值,并且必须记得用R_c.mx访问数组。
关于常量M_PI
C 标准不仅没有定义这个常量,而且规定一个符合要求的实现必须默认不定义它。这意味着如果一个特定的实现确实定义了M_PI,它是一个必须显式启用的扩展。作为一个通用扩展,定义了这个常量。例如,如果使用gcc -std=gnu11 编译,则无需显式启用M_PI。但如果使用更严格的选项之一,例如,gcc -std=c11、M_PI 必须显式启用。 Here is an SO question that discusses the issue.
现在,_USE_MATH_DEFINES 适用于 Microsoft 实现,但不适用于 Linux 中的 GCC(据我所知)。如需更多便携性,请note that M_PI is defined in POSIX。这可以通过使用功能测试宏_XOPEN_SOURCE 700 来启用。在源文件的第一行添加:
#define _XOPEN_SOURCE 700
或在编译时从命令行启用:
gcc -std=c11 -D_XOPEN_SOURCE=700
请注意,当使用#define 启用功能时,它必须位于源文件的最开头才能工作。当然这里也可以用-std=c99或-std=c89代替std=c11。
这将适用于与 POSIX 密切相关的 Linux 系统,但我不确定它是否适用于与 POSIX 不太密切相关的 Microsoft 系统。为了获得最大的可移植性,最好简单地明确定义常量,使用:
#define M_PI 3.14159265358979323846
一个示例程序
这是一个说明上述每种方法的程序:
#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');
}
}
程序输出:
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