【问题标题】:How to allocate and deallocate heap memory for 2D array?如何为二维数组分配和释放堆内存?
【发布时间】:2011-11-05 12:35:11
【问题描述】:

我习惯了 PHP,但我开始学习 C。我正在尝试创建一个程序,它可以逐行读取文件并将每一行存储到一个数组中。

到目前为止,我有一个程序可以逐行读取文件,甚至打印每一行,但现在我只需要将每一行添加到一个数组中。

昨晚我的朋友告诉了我一些关于这件事的事情。他说我必须在 C 中使用多维数组,所以基本上是array[x][y][y] 部分本身很简单,因为我知道每行的最大字节数。但是,我不知道该文件将有多少

我想我可以让它循环遍历文件,每次只增加一个整数并使用它,但我觉得可能有更简单的方法。

有任何想法甚至是正确方向的提示吗?感谢您的帮助。

【问题讨论】:

  • 以后可以使用realloc函数来改变数组的大小。
  • 我会去查找那个函数并尝试考虑如何实现它,我会回复你,谢谢

标签: c arrays file loops multidimensional-array


【解决方案1】:

您可以使用mallocrealloc 函数动态分配和调整指向char 的指针数组,并且数组的每个元素都将指向从文件中读取的字符串(该字符串的存储位置是也动态分配)。为简单起见,我们假设每行的最大长度小于 M 个字符(包括换行符),因此我们不必对各个行进行任何动态调整大小。

每次扩展数组时,您都需要手动跟踪数组大小。一种常见的技术是每次扩展时将数组大小加倍,而不是按固定大小扩展;这最大限度地减少了对realloc 的调用次数,这可能是昂贵的。当然,这意味着您必须跟踪两个数量;数组的总大小和当前读取的元素个数。

例子:

#define INITIAL_SIZE ... // some size large enough to cover most cases

char **loadFile(FILE *stream, size_t *linesRead)
{
  size_t arraySize = 0;   
  char **lines = NULL;
  char *nextLine = NULL;

  *linesRead = 0;

  lines = malloc(INITIAL_SIZE * sizeof *lines);
  if (!lines)
  {
    fprintf(stderr, "Could not allocate array\n");
    return NULL;
  }

  arraySize = INITIAL_SIZE;

  /**
   * Read the next input line from the stream.  We're abstracting this
   * out to keep the code simple.
   */
  while ((nextLine = getNextLine(stream)))  
  {
    if (arraySize <= *linesRead)
    {
      char **tmp = realloc(lines, arraysSize * 2 * sizeof *tmp);
      if (tmp)
      {
        lines = tmp;
        arraySize *= 2;
      }
    }
    lines[(*linesRead)++] = nextLine;
  )

  return lines;
}

【讨论】:

    【解决方案2】:

    虽然多维数组可以解决这个问题,但矩形二维数组并不是真正的自然 C 解决方案。

    这是一个程序,它最初将文件读入一个链表,然后分配一个大小合适的指针向量。然后每个单独的字符确实显示为array[line][col],但实际上每一行只是它需要的长度。除了&lt;err.h&gt;,它是 C99。

    #include <err.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct strnode {
      char *s;
      struct strnode *next;
    } strnode;
    
    strnode *list_head;
    strnode *list_last;
    
    strnode *read1line(void) {
      char space[1024];
      if(fgets(space, sizeof space, stdin) == NULL)
        return NULL;
      strnode *node = malloc(sizeof(strnode));
      if(node && (node->s = malloc(strlen(space) + 1))) {
        strcpy(node->s, space);
        node->next = NULL;
        if (list_head == NULL)
          list_head = node;
        else
          list_last->next = node;
        list_last = node;
        return node;
      }
      err(1, NULL);
    }
    
    int main(int ac, char **av) {
      int n;
      strnode *s;
    
      for(n = 0; (s = read1line()) != NULL; ++n)
        continue;
      if(n > 0) {
        int i;
        strnode *b;
        char **a = malloc(n * sizeof(char *));
        printf("There were %d lines\n", n);
        for(b = list_head, i = 0; b; b = b->next, ++i)
          a[i] = b->s;
        printf("Near the middle is: %s", a[n / 2]);
      }
      return 0;
    }
    

    【讨论】:

      【解决方案3】:

      这里不用数组,也可以用链表,代码比较简单,但是分配比较频繁,容易产生碎片。

      只要你不打算做太多的随机访问(这里是 O(n)),迭代就和普通数组一样简单。

      typedef struct Line Line;
      struct Line{
          char text[LINE_MAX];
          Line *next;
      };
      
      Line *mkline()
      {
          Line *l = malloc(sizeof(Line));
          if(!l)
             error();
          return l;
      }
      
      main()
      {
          Line *lines = mkline();
          Line *lp = lines;
          while(fgets(lp->text, sizeof lp->text, stdin)!=NULL){
               lp->next = mkline();
               lp = lp->next;
          }
          lp->next = NULL;
      }
      

      【讨论】:

        【解决方案4】:

        C 中没有标准的可调整大小的数组类型。您必须自己实现它,或者使用第三方库。这是一个简单的简单示例:

        typedef struct int_array
        {
            int *array;
            size_t length;
            size_t capacity;
        } int_array;
        
        void int_array_init(int_array *array)
        {
            array->array = NULL;
            array->length = 0;
            array->capacity = 0;
        }
        
        void int_array_free(int_array *array)
        {
            free(array->array);
            array->array = NULL;
            array->length = 0;
            array->capacity = 0;
        }
        
        void int_array_push_back(int_array *array, int value)
        {
            if(array->length == array->capacity)
            {
                // Not enough space, reallocate.  Also, watch out for overflow.
                int new_capacity = array->capacity * 2;
                if(new_capacity > array->capacity && new_capacity < SIZE_T_MAX / sizeof(int))
                {
                    int *new_array = realloc(array->array, new_capacity * sizeof(int));
                    if(new_array != NULL)
                    {
                       array->array = new_array;
                       array->capacity = new_capacity;
                    }
                    else
                        ; // Handle out-of-memory
                }
                else
                    ; // Handle overflow error
            }
        
            // Now that we have space, add the value to the array
            array->array[array->length] = value;
            array->length++;
        }
        

        像这样使用它:

        int_array a;
        int_array_init(&a);
        
        int i;
        for(i = 0; i < 10; i++)
            int_array_push_back(&a, i);
        for(i = 0; i < a.length; i++)
            printf("a[%d] = %d\n", i, a.array[i]);
        
        int_array_free(&a);
        

        当然,这仅适用于ints 的数组。由于 C 没有模板,因此您必须将所有这些代码放在每个不同类型数组的宏中(或使用不同的预处理器,例如 GNU m4)。或者,您可以使用一个通用数组容器,该容器使用void* 指针(要求所有数组元素都是malloc'ed)或不透明的内存blob,这需要对每个元素访问进行强制转换,并为每个元素使用memcpy元素获取/设置。

        无论如何,它并不漂亮。二维数组就更丑了。

        【讨论】:

          【解决方案5】:

          动态分配二维数组:

          char **p;
          int i, dim1, dim2;
          
          
          /* Allocate the first dimension, which is actually a pointer to pointer to char   */
          p = malloc (sizeof (char *) * dim1);
          
          /* Then allocate each of the pointers allocated in previous step arrays of pointer to chars
           * within each of these arrays are chars
           */
          for (i = 0; i < dim1; i++)
            {
              *(p + i) = malloc (sizeof (char) * dim2);
             /* or p[i] =  malloc (sizeof (char) * dim2); */
            }
          
           /* Do work */
          
          /* Deallocate the allocated array. Start deallocation from the lowest level.
           * that is in the reverse order of which we did the allocation
           */
          for (i = 0; i < dim1; i++)
          {
            free (p[i]);
          }
          free (p);
          

          修改上面的方法。当您需要添加另一行时,请执行 *(p + i) = malloc (sizeof (char) * dim2); 并更新 i。在这种情况下,您需要预测文件中由dim1 变量指示的最大行数,我们第一次为此分配p 数组。这只会分配(sizeof (int *) * dim1) 字节,因此比char p[dim1][dim2](在c99 中)更好。

          我认为还有另一种方式。以块为单位分配数组并在溢出时将它们链接起来。

          struct _lines {
             char **line;
             int n;
             struct _lines *next;
          } *file;
          
          file = malloc (sizeof (struct _lines));
          file->line = malloc (sizeof (char *) * LINE_MAX);
          file->n = 0;
          head = file;
          

          在此之后,第一个块就可以使用了。当您需要插入一行时,只需执行以下操作:

          /* get line into buffer */
          file.line[n] = malloc (sizeof (char) * (strlen (buffer) + 1));
          n++;
          

          nLINE_MAX 时,分配另一个块并将其链接到这个块。

          struct _lines *temp;
          
          temp = malloc (sizeof (struct _lines));
          temp->line = malloc (sizeof (char *) * LINE_MAX);
          temp->n = 0;
          file->next = temp;
          file = file->next;
          

          类似的东西。

          当一个块的n变为0时,释放它,并将当前块指针file更新为前一个。您可以从单链表开始遍历,也可以从头开始遍历,也可以使用双链表。

          【讨论】:

          • 我相信,当输入文件不可搜索(例如管道或标准输入)时,后一种方法是 GNU 的 tail(1) 实现的工作方式。由于在这些情况下它只能通过输入文件,因此它将文件存储在内存 blob 的链接列表中,当它到达文件结尾时,它向后搜索以打印出最后的N 行。
          • @Adam Rosenfield:不知道,感谢您提供的信息。很久以前我就用它在内存中存储了一长串单词,它非常有用。
          【解决方案6】:

          如果您使用的是 C,您将需要自己实现数组的大小调整。 C++ 和 SDL 已经为您完成了这项工作。它被称为vectorhttp://www.cplusplus.com/reference/stl/vector/

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-03-15
            • 2018-10-05
            • 2015-07-09
            • 2015-02-01
            • 1970-01-01
            • 1970-01-01
            • 2020-05-10
            相关资源
            最近更新 更多