C上的返回矩阵

时间:2017-09-16 06:35:20

标签: c arrays matrix return-value

以下是我的代码。

#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;
}

目前,函数rot本身不返回任何值。 但由于R是静态双倍,我能够打印出由rot函数改变的R. 我想改变函数rot以返回R [3] [3](二维数组)。 所以我想在主要像

上使用rot函数
double R1=rot(30);
double R2=rot(60);

有什么办法可以实现吗?

2 个答案:

答案 0 :(得分:4)

副本不一定对此有好处。由于这些R²矩阵需要易于复制,因此表示它们的最佳方法是将它们放在 struct 中:

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

然后将它们作为返回值是微不足道的:

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;
}

与数组不同,包含数组的结构也很容易复制:

Matrix3x3 x = y;

答案 1 :(得分:3)

C不允许从函数返回数组。可以在调用函数中分配数组,也可以在函数中动态分配数组,并返回指向数组的指针。请注意,函数中定义的数组是本地的,并且返回指向此类数组的指针是无用的,因为函数返回后局部变量不再存在。另一方面,动态分配继续存在。第三种选择是使用以下事实:虽然不能从C中的函数返回数组,但struct可以从函数返回

在调用者

中定义一个数组

一个简单的解决方案是在调用函数中定义一个数组。该数组将在rot_3x3_a()函数中修改。请注意,不是依赖函数的用户记住对数组进行零初始化,而是在函数内使用memset()(在string.h中定义)来将数组归零。

使用此方法的优点是可以避免动态分配,并且需要稍后解除分配以避免内存泄漏。所有方法都需要在调用函数中声明一个变量来接收旋转矩阵,但是这个方法也要求将数组传递给函数,因此这可能不符合OP要求。

动态分配

更复杂的解决方案是使用动态分配。正如OP示例所示,rot_3x3_b()函数只接受int参数,但返回指向3 double s数组的指针(这是3x3数组衰减到的指针类型)在大多数表达式中)。此处使用typedef,因此Matrix_3x3是指向3 double s数组的指针;这样可以更容易地编写函数原型。

此处memset()可能与malloc()一起使用,但已使用calloc(),它会自动对分配进行零初始化。请注意,在rot_3x3_b()函数中,如果分配失败,则不会发生任何其他情况,并返回空指针。调用者必须检查并正确处理错误。

使用这种方法的优点是结果矩阵分配可以简单地索引为2d数组,并且函数的参数符合OP要求。缺点是代码比其他解决方案稍微复杂一些,并且更容易出错,并且此函数的用户必须记住free已分配的内存以避免内存泄漏。

在结构中包装数组

虽然C不允许将数组分配给另一个数组,但可以将struct分配给另一个struct。此外,struct是可以从函数返回的左值。因此,该问题的一个解决方案是在struct函数内创建包含3x3数组的rot_3x3_c()。在从函数返回struct之前,可以填充struct的数组成员。在这种情况下,已使用指定的初始化程序对struct的数组成员进行零初始化。

这里的优点是实施简单。没有动态分配,因此无需担心内存泄漏。缺点是数组必须作为struct成员访问,因此该函数的用户必须声明struct Mtrx_3x3 R_c以接收从函数返回的值,并且必须记住使用{{访问该数组1}}。

关于常数R_c.mx

C标准不仅没有定义这个常量,而是规定一致的实现必须默认不定义它。这意味着如果特定实现确实定义了M_PI,则它是必须明确启用的扩展。作为公共扩展,定义了此常量。例如,如果使用M_PI进行编译,则无需显式启用gcc -std=gnu11。但是,如果使用其中一个更严格的选项,例如M_PI,则必须明确启用gcc -std=c11Here is an SO question that discusses the issue

现在,M_PI适用于Microsoft实现,但不适用于Linux中的GCC(据我所知)。为了更具便携性,note that M_PI is defined in POSIX。可以使用功能测试宏_USE_MATH_DEFINES启用此功能。要么添加为源文件中的第一行:

_XOPEN_SOURCE  700

或在编译时使用命令行启用:

#define _XOPEN_SOURCE  700

请注意,使用gcc -std=c11 -D_XOPEN_SOURCE=700 启用功能时,它必须位于源文件的最开头才能工作。当然,此处也可以使用#define-std=c99代替-std=c89

这将适用于与POSIX密切相关的Linux系统,但我不确定它是否适用于与POSIX紧密相关的Microsoft系统。为了获得最大的可移植性,最好使用:

显式定义常量
std=c11

示例程序

这是一个程序,说明了上述每种方法:

#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');
    }
}