在C语言中从函数返回一个矩阵

3

我正在编写一个程序,需要在函数中不断地处理矩阵。这是其中的一个函数,它的作用是打开一个外部数据集文件,该文件中的数据以制表符分隔,打开文件并将数据保存到矩阵M中。我知道这个矩阵由6列组成,但行数未知。我知道错误出现在声明矩阵时,因为函数需要返回矩阵,所以必须使用指针来声明。

//type float** since it will return a matrix
float **carga_archivo(char *nombre_archivo)       
{
  float **M=(float **)malloc(6*sizeof(float*));  //error should be here
  int i=0;
  FILE *archivo;                      //FILE type pointer to open the external file
  archivo=fopen(nombre__archivo,"r"); //Opens the file in the address indicated
                                      //"nombre_de_archivo" is a variable
  while(!feof(archivo))               //Browses the file row per row till the end of it
  {
      //saves the data in its corresponding place in the matrix
      fscanf(archivo,"%f\t%f\t%f\t%f\t%f\t%f\n",
          &M[0][i],&M[1][i],&M[2][i],&M[3][i],&M[4][i],&M[5][i]);
      i++;
  }
  tam=i;
  fclose (archivo);                  //closes the file
  return M;
}

我需要的是声明矩阵的正确方法。
附言:我在代码中记录了主要内容,以帮助需要类似东西的人。欢迎任何纠正。
更新:应用了评论中提出的一些更改,效果更好了。这是我为该函数制作的新代码。
float **carga_archivo(char *nombre_archivo)
{
int i=0;
float P[300][6];
FILE *archivo;
archivo=fopen(nombre_archivo,"r");
 while(!feof(archivo))
{
i++;
       //this was just so the feof function could browse row per row 
       //instead of character per character
    scanf("%f\t%f\t%f\t%f\t%f\t%f\n",
               &P[0][i],&P[1][i],&P[2][i],&P[3][i],&P[4][i],&P[5][i]);
    printf("%i\n",i);
}
tam=i;
printf("%i",tam);
int filas = 6;
int columnas = tam;
float **M;

M = (float **)malloc(filas*sizeof(float*));

for (i=0;i<filas;i++)
    M[i] = (float*)malloc(columnas*sizeof(float));

for (i = 0; i < columnas; ++i)
    fscanf(archivo,"%f\t%f\t%f\t%f\t%f\t%f\n",
             &M[0][i],&M[1][i],&M[2][i],&M[3][i],&M[4][i],&M[5][i]);
fclose (archivo);
return M;
}

新的问题是当调用该函数时,程序实际上会编译,但在运行并调用函数时程序崩溃停止。 这里是调用该函数的代码部分。
int main()
{
int i,j;
char *nombre_archivo="Agua_Vapor.txt";
float **agua_vapor=carga_archivo(nombre_archivo);
for (i = 0; i < 6; i++)
{
    for (j = 0; i < tam; i++)
        printf("%f   ", agua_vapor[i][j]);
    printf("\n");
}
return 0;
}

2
char nombre_archivo 是错误的。文件名不止一个字符。此外,如果行数未知--您需要将该信息返回给调用者,可能作为返回值(同时传递指向函数的指针,该指针在函数返回后将指向浮点数数组)。 - John Coleman
2
如果您事先不知道行数,那么您需要至少扫描一次文件以获取该数字,然后分配存储数据所需的内存量,然后将数据从文件读入矩阵中。这需要两次读取文件。 - user707650
补充Evert的回答,你可以读取文件一次,但需要将数据存储在本地存储中。你可以定义一个单独的函数,可以将值添加到动态增长的数组中。一旦从文件复制完成。分配一个二维数组[row][column]并复制内容。 - shobhit
@shobhit但是如果您事先不知道文件大小,如何将数据存储在本地存储中? - user707650
@evert,我更新了我的评论。当您读取文件时,可以通过保持计数器来知道大小。您可以通过创建类似于C++向量的函数来将内容存储在动态增长的数组中。这是在读取文件两次或制作动态增长向量之间进行权衡的一种方式。 - shobhit
显示剩余3条评论
1个回答

2
你的程序存在未定义的行为,因为你正在填充由未初始化指针引用的内存。
既然你知道总是有6列,一个简单的方法是将矩阵按行主序存储而不是按列主序(你的示例是按列主序)。这意味着你可以将矩阵数据存储为一大块内存,并在必要时使用realloc。你可能还想为此创建一个简单的结构体。
struct matrix {
    int rows, cols;
    float ** data;
};

然后动态创建它。
struct matrix * matrix_alloc( int rows, int cols )
{
    int i;

    struct matrix * m = malloc(sizeof(struct matrix));
    m->rows = rows;
    m->cols = cols;
    m->data = malloc(rows * sizeof(float*));
    m->data[0] = malloc(rows * cols * sizeof(float));

    for( i = 1; i < rows; i++ ) {
        m->data[i] = m->data[i-1] + cols; 
    }
    return m;   
}

void matrix_free( struct matrix * m )
{
    free( m->data[0] );
    free( m->data );
    free( m );
}

现在,当您决定需要添加存储以容纳更多行时:

void matrix_set_row_dimension( struct matrix * m, int rows )
{
    float **new_index, *new_block;
    new_index = realloc(m->data, rows * sizeof(float**));
    new_block = realloc(m->data[0], rows * m->cols * sizeof(float));
    if( new_index && new_block )
    {
        int i = m->rows;
        m->rows = rows;
        m->data = new_index;

        /* if block address changed, prepare to reindex entire block */
        if( m->data[0] != new_block )
        {
            m->data[0] = new_block;
            i = 1;
        }

        /* reindex */
        for( ; i < rows; i++ ) {
            m->data[i] = m->data[i-1] + cols;
        }
    }
}

所以,现在当您填充矩阵时...
struct matrix * m = matrix_alloc( 10, 6 );  /* Start with 10 rows */
int row = 0;
while( 1 ) {
    /* Double matrix row count if not large enough */
    if( row == m->rows )
    {
        matrix_set_row_dimension( m, m->rows * 2 );

        /* Check for error here */
    }

    /* Now the matrix has enough storage to continue adding */
    m->data[row][0] = 42;
    m->data[row][1] = 42;
    m->data[row][2] = 42;
    m->data[row][3] = 42;
    m->data[row][4] = 42;
    m->data[row][5] = 42;

    row++;     
}

1
注意:这不是硬化代码。需要适当处理所有重新分配失败的情况,以及处理初始分配失败的情况。这里的代码纯粹是说明一个合理的方法。 - paddy
1
注意:m->data[0] = malloc(sizeof(float) * rows * cols); 相对于 m->data[0] = malloc(rows * cols * sizeof(float)); 有一个小优势,即 sizeof(float) * rows * cols 通常不会像 rows * cols * sizeof(float) 那样很快溢出。 - chux - Reinstate Monica

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