如何在C语言中从函数返回一个二维数组?

32

我是一名Ruby程序员,最近开发了一个用于生成C代码的工具。这就好比让豪华轿车拖曳上世纪60年代的卡车。无论如何,下面是我认为应该有效但实际并不行的代码:

float[][] pixels()
{
  float x[][]= { {1,1},{2,2} };
  return x
}

void drawLine(float x[][2])
{
  //drawing the line
}

//inside main
drawLine(pixels());

我一直在试图让这件事情正常工作,但是一直碰壁。请帮忙。


4
这可能会引起您的兴趣:https://dev59.com/jG455IYBdhLWcg3wCfqC。该问题讨论了指向局部变量的指针,这可能导致未定义的行为。在函数返回后使用指向局部变量的指针将导致不可预测的结果。建议使用指向堆中分配的内存或静态变量的指针,以避免此类问题。 - jweyrich
有什么问题吗? - Bala R
请阅读Mahesh的答案以了解问题所在。 - Eastern Monk
5个回答

39

C 语言中,指针和数组密切相关。通常情况下,你需要将数组的大小作为一个独立的变量传递。让我们开始吧:

#include <stdio.h>

float** createArray(int m, int n)
{
    float* values = calloc(m*n, sizeof(float));
    float** rows = malloc(m*sizeof(float*));
    for (int i=0; i<m; ++i)
    {
        rows[i] = values + i*n;
    }
    return rows;
}

void destroyArray(float** arr)
{
    free(*arr);
    free(arr);
}

void drawLine(const float** coords, int m, int n);

int main(void)
{
    float** arr = createArray(2,2);
    arr[0][0] = 1;
    arr[0][1] = 1;
    arr[1][0] = 2;
    arr[1][1] = 2;
    drawLine(arr, 2, 2); 
    destroyArray(arr);
}

我刚试了一下这段代码片段,得到了一个错误:"error: invalid conversion from ‘void’ to ‘float’" 在 createArray 中。你需要添加一个强制类型转换:"float* values = (float*)calloc(mn, sizeof(float));" .. 对于 float* 也是同样的情况。 - Alex
2
@Alex 你是否将它编译为C++代码?这是有效的C代码,但无效的C++代码。 - aschepler
float** 是二维浮点数组的“类型”吗? - Emil S.

8
感谢大家的回答,尤其是对于数组指针关系的详细解释。
我将数组封装在一个结构中。
 struct point_group1 {
        float x[3];
        float y[3];
};

struct point_group1 pixels(){
    struct point_group1 temp;

    temp.x[0] = 0.0;
    temp.x[1] = 1.0;
    temp.x[2] = -1.0;

    temp.y[0] = 0.0;
    temp.y[1] = 1.0;
    temp.y[2] = 1.0;

    return temp;    
}



struct point_group1 points1  = pixels();
axPoly(points1.x, points1.y ,3, 0.0);

这是一个相当不错的解决方案,只需注意如果这些是大数组并且您的编译器不擅长优化,则可能会创建不必要的副本。 - user470379

6
在C / C ++中,当您将数组传递给函数时,它会衰变为指向数组第一个元素的指针。因此,在pixels()函数中,您正在返回堆栈分配变量的地址。返回变量的地址不再有效,因为在pixels()返回时,堆栈分配的变量超出了范围。因此,您应该使用存储是动态的变量(即使用malloc、calloc)。对于二维数组,您可以使用float ** arrayVariable;。此外,如果将其传递给函数,您应该注意它有多少行和列。
int rows, columns;

float** pixels()
{
    // take input for rows, columns
    // allocate memory from free store for the 2D array accordingly
    // return the array
}

void drawLine( float** returnedArrayVariable )
{
  //drawing the line
}

由于2D数组自己管理资源,因此应使用free将资源返回给自由存储器。


我猜你希望我在像素函数内使用malloc。那么我需要记得释放它。 - Eastern Monk

6
float (*pixels(void))[2] 
{
  static float x[2][2]= { {1,1},{2,2} };
  return x;
}

void drawLine(float (*x)[2])
{
  //drawing the line
  //x[0][0];
}

//inside main
drawLine(pixels());

1
你的答案是唯一正确的!可惜你没有解释任何东西。如果你添加了一些解释,请随时在评论中告诉我,我会检查并点赞,如果我认为它很好的话。 - too honest for this site

1

最简单的方法可能是在主函数中声明 float 数组,并让 pixels 填充它:

#define PIXEL_X_SIZE 2
#define PIXEL_Y_SIZE 2

int pixels(float x[][PIXEL_X_SIZE], int len) {
    /* I don't know if you want the logic of this method to ever change,
       but this will be roughly equivalent to what you do above */
    if (len < PIXEL_Y_SIZE) {
        /* the length of the passed array is too small, abort */
        return -1;
    }

    x[0][0] = x[0][1] = 1;
    x[1][0] = x[1][1] = 2;
    return 0;
}

void drawLine(float x[][PIXEL_X_SIZE]) {
    /* this will work fine */
}

int main() {
    float pixel_array[PIXEL_Y_SIZE][PIXEL_X_SIZE];
    pixels(pixel_array, PIXEL_Y_SIZE);
    drawLine(pixel_array);
}

你也可以使用mallocfree在堆上存储像素,但如果像素数组不会太大,那么实际上并没有必要这样做,而且这只会增加额外的复杂性,以确保内存始终得到正确分配和释放。


你的方法很好。我唯一担心的是,由于我们的应用程序预计在嵌入式设备上运行,代码审查清单要求最小化全局变量。代码将有数百个像素一样的函数。 - Eastern Monk
@Akshar 我不使用任何全局变量... 我只是定义了一些东西,但如果你真的想的话,你可以在每个地方都重复写2,但是一旦这些值有所改变,你将会面临很大的维护问题,并且如果有人错误地设置了新的尺寸,调试错误将会非常困难。 - user470379

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接