C中未知矩阵的动态分配

Dynamic allocation of an unknown matrix in C

我需要将用户输入的文件乘以另一个文件。我知道怎么做了。

问题是一个文件是数组,另一个是矩阵。

我需要扫描矩阵的第一行来找到矩阵的大小,然后我需要从文件中动态分配矩阵和数组。

这是我目前拥有的:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int main()
{       
    int row1, col1;
        //These values need to be pulled from the first file//
    char filename1[100];
        //Setting the file name for entry and setting the limit to 100//
    FILE* fp1;
        //FILE must be set as a pointer (FILE must also be capitalized)//

    printf("Enter file name including file extension: \n");
        //This will pull in the name entered by the user//
    scanf("%s", filename1);
        //Scans in the name of the first file//

    fp1 = fopen(filename1, "r");
        //This will open the file as entered by the user//
    if (fp1 == NULL)
    {
        printf("\nError, file not found\n");
        exit(0);
    }
        //This is for the first file//

    char filename2[100];
        //Setting the file name for entry and setting the limit to 100//
    FILE* fp2;
        //FILE must be set as a pointer (FILE must also be capitalized)//

    printf("Enter file name including file extension: \n");
        //This will pull in the name entered by the user//
    scanf("%s", filename2);
        //Scans in the name of the first file//

    fp2 = fopen(filename2, "r");
        //This will open the file as entered by the user//
    if (fp2 == NULL)
    {
        printf("\nError, file not found\n");
        exit(0);
    }
        //This is for the second file//

        //**I need to now dynamically allocate the input files**//

    return 0;
} 

也很抱歉,因为一些成员在评论中分享说我在代码钓鱼,所以看起来我在发布问题后就离开了。我不是;我只是没有意识到这个社区有多活跃。感谢您到目前为止的投入。

Here is the screenshot of all I have so far including the files that are going to be read in.

感谢您的建议。我能够找出 "fgets" 函数,并用它从第一个文件中提取矩阵的大小。在我有了之后,动态分配就很容易了。

我没看到你在哪里实际读取了 row/column 计数,但是一旦你有了它们,分配就很简单了:

int (*matrix)[columnCount] = malloc(rowCount*sizeof(*matrix));

就是这样。这声明 matrix 是指向 columnCount 整数数组的指针 *matrix。括号是必需的,因为 int* matrix[...] 会声明一个指针数组。 malloc()rowCount 这样的数组分配 space,在一块内存中为您提供完整的二维矩阵。访问与任何二维数组一样:

for(int y = 0; y < rowCount; y++) {
    for(int x = 0; x < columnCount; x++) {
        matrix[y][x] = 42;
    }
}

释放和分配一样简单:

free(matrix);

我的建议是将您的矩阵视为具有您想要实现的一些 abstract data type

一种常见的方法可能是使用指针数组(指向表示矩阵行的数组)。但是我觉得它很混乱而且效率低下。

那么您希望对矩阵进行哪些操作?

  • 创建给定维度的矩阵

  • 销毁先前创建的矩阵

  • 使用给定的行索引和列索引访问给定矩阵中的某些元素

  • 使用给定的行和列索引更改给定矩阵中元素的值

  • 等....

顺便说一句,您可能有几种变体。例如,您可以进行错误检查(例如拒绝负索引)或者您可以使用不安全(但速度稍快)的函数 undefined behavior (and this is very scary)。当然你可以定义更多的操作(使用其他的),例如矩阵乘法等

您应该在纸上或纸板上列出-所有您想要在矩阵上进行的操作,并在您的文档(或您的评论)中解释它们。在实践中,您可能对抽象数据类型进行数十次甚至数百次操作。还要记录错误情况下发生的情况。

我通常建议保持矩阵的维度(除非你知道某些维度是常数)。在 C 中实现抽象数据类型的一种常见方法是将它们封装在一些 struct 中并使用指向这些的指针。

所以我建议使用 flexible array member(作为 structlast 元素)。这是我的 matrix_st 结构:

  struct matrix_st {
    unsigned m_h, m_w; // height and width of matrix
    double m_v[]; // values inside the matrixes, there are m_h*m_w of them
  };

所以我的抽象数据类型只是指向

的指针
  typedef struct matrix_st Matrix;

下面是实现我的抽象数据类型的函数声明:

  Matrix* matrix_create(unsigned height, unsigned width);
  void matrix_destroy(Matrix*mat);
  double matrix_access(Matrix*mat, unsigned i, unsigned j);
  void matrix_change_element(Matrix*mat, unsigned i, unsigned j,double v);

这里有一些实现(因为我不想处理病态的巨大矩阵,我定义了一些最大维度;计算机资源总是有限的!):

  #define MATRIX_MAXDIM 10000000 /* ten millions */
  Matrix* matrix_create(unsigned height, unsigned width) {
     if (height>MATRIX_MAXDIM || width>MATRIX_MAXDIM) {
        fprintf(stderr, "too huge matrix height=%u width=%u\n",
                height, width);
        exit(EXIT_FAILURE);
     };
     Matrix* res = 
        calloc(1, sizeof(Matrix) + height*width*sizeof(double));
     if (!res) {
         perror("matrix calloc");
         exit(EXIT_FAILURE);
     };
     res->m_h = height;
     res->m_w = width;
     return res; 
  } // end matrix_create

我使用的是 calloc 而不是 malloc 因为我真的想要一些归零内存。所以返回的矩阵包含全零。顺便说一句,在某些计算机上(不是我的计算机,PC/Linux/Debian/x86-64 台式机)height*width*sizeof(double) 可能会溢出。

这是访问某些元素的函数。它会进行一些错误检查。

double matrix_access(Matrix*mat, unsigned i, unsigned j) 
{ 
   if (!mat) 
      { fprintf(stderr, "no matrix to access\n"); exit(EXIT_FAILURE; };
   unsigned h = mat->m_h;
   unsigned w = mat->m_w;
   if (i >= h || j >= w)
      { fprintf(stderr, "out-of-bound matrix access\n"); 
        exit(EXIT_FAILURE); };
   return mat->m_v [i*h + j];
}

因为我只做了一个 calloc 销毁代码很简单:

  void matrix_destroy(Matrix*mat) {
    if (!mat) { fprintf(stderr, "no matrix to destroy\n"); exit(EXIT_FAILURE); };
    assert (mat->m_h < MATRIX_MAXDIM);
    assert (mat->m_w < MATRIX_MAXDIM);
    free (mat);
  }

assert 语句原则上是无用的(它们检查应该 总是 为真的东西)。但我喜欢 defensive programming (this would help me catching bugs in some other places misusing my Matrix). They could be disabled (read assert(3)) 在编译时。

顺便说一句,您可以将这些函数声明为 inlinestatic inline(并在某些包含的头文件中定义它们)。基准测试时 optimizing compiler is likely to produce efficient code (e.g. compile with gcc -O2 -Wall -march=native)。

由于您正在从某个文件中读取矩阵,因此您应该定义 file format (using, in your documentation, some EBNF 符号来描述该文件中的语法很有用)并且您可以定义和实现一个函数,从某些文件中读取和创建矩阵打开的文件句柄。


编写其他函数的代码作为练习留给 reader。

不要忘记在编译时编译 with all warnings and debug info, so gcc -Wall -Wextra -g with GCC. Use the debugger gdb (and also valgrind to hunt memory leaks). Read the documentation of every used function (for example your code don't check the return count of scanf but it really should). Run several test cases. Try to convince yourself that your code is good (by proving parts of it). Perhaps use some static source code analyzer (e.g. Frama-C, which wants extra annotations in ACSL). If you need to benchmark your program, enable optimizations(例如,将 -O2 -march=native 传递给 gcc ....)。


在您提出的代码注释中:

 // I need to now dynamically allocate the input files

你不分配输入 files (the operating system is managing them), you allocate some memory zone. Read about C dynamic memory allocation. Notice that memory allocation can fail (e.g. as documented in malloc(3)), because your virtual address space 不能无限增长。

顺便说一句,call stack is limited (typically to a megabyte or a few of them on desktop computers), so you generally want to avoid large automatic variables,所以这是避免在调用框架中放置矩阵并更喜欢为它们动态分配内存的另一个很好的理由。