C语言 如何为二维数组分配和释放堆内存?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7221981/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 09:31:04  来源:igfitidea点击:

How to allocate and deallocate heap memory for 2D array?

carraysfileloopsmultidimensional-array

提问by Rob

I'm used to PHP, but I'm starting to learn C. I'm trying to create a program that reads a file line by line and stores each line to an array.

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

So far I have a program that reads the file line by line, and even prints each line as it goes, but now I just need to add each line to an array.

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

My buddy last night was telling me a bit about it. He said I'd have to use a multidimensional array in C, so basically array[x][y]. The [y]part itself is easy, because I know the maximum amount of bytes that each line will be. However, I don't know how many linesthe file will be.

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

I figure I can make it loop through the file and just increment an integer each time and use that, but I feel that there might be a more simple way of doing it.

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

Any ideas or even a hint in the right direction? I appreciate any help.

任何想法甚至是正确方向的提示?我很感激任何帮助。

采纳答案by phoxis

To dynamically allocate a 2D array:

动态分配二维数组:

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

Modify the above method. When you need another line to be added do *(p + i) = malloc (sizeof (char) * dim2);and update i. In this case you need to predict the max numbers of lines in the file which is indicated by the dim1variable, for which we allocate the parray first time. This will only allocate the (sizeof (int *) * dim1)bytes, thus much better option than char p[dim1][dim2](in c99).

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

There is another way i think. Allocate arrays in blocks and chain them when there is an overflow.

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

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;

After this the first block is ready to use. When you need to insert a line just do:

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

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

When nis LINE_MAXallocate another block and link it to this one.

如果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;

Something like this.

像这样的东西。

When one block's nbecomes 0, deallocate it, and update the current block pointer fileto the previous one. You can either traverse from beginning single linked list and traverse from the start or use double links.

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

回答by Adam Rosenfield

There's no standard resizable array type in C. You have to implement it yourself, or use a third-party library. Here's a simple bare-bones example:

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

Use it like this:

像这样使用它:

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

Of course, this is only for an array of ints. Since C doesn't have templates, you'd have to either put all of this code in a macro for each different type of array (or use a different preprocessor such as GNU m4). Or, you could use a generic array container that either used void*pointers (requiring all array elements to be malloc'ed) or opaque memory blobs, which would require a cast with every element access and a memcpyfor every element get/set.

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

In any case, it's not pretty. Two-dimensional arrays are even uglier.

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

回答by Dave

Instead of an array here, you could also use a linked list, The code is simpler, but the allocation is more frequent and may suffer from fragmentation.

这里也可以用链表代替数组,代码更简单,但分配更频繁,可能会出现碎片。

As long as you don't plan to do much random access (Which is O(n) here), iteration is about as simple as a regular array.

只要你不打算做太多的随机访问(这里是 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;
}

回答by DigitalRoss

While a multidimensional array can solve this problem, a rectangular 2D array would not really be the natural C solution.

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

Here is a program that initially reads the file into a linked list, and then allocates a vector of pointers of the right size. Each individual character does then appear as array[line][col]but in fact each row is only as long as it needs to be. It's C99 except for <err.h>.

这是一个程序,它最初将文件读入一个链表,然后分配一个大小合适的指针向量。每个单独的字符确实会出现,array[line][col]但实际上每一行都只有它需要的长度。除了<err.h>.

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

回答by John Bode

You can use the mallocand reallocfunctions to dynamically allocate and resize an array of pointers to char, and each element of the array will point to a string read from the file (where that string's storage is also allocated dynamically). For simplicity's sake we'll assume that the maximum length of each line is less than M characters (counting the newline), so we don't have to do any dynamic resizing of individual lines.

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

You'll need to keep track of the array size manually each time you extend it. A common technique is to double the array size each time you extend, rather than extending by a fixed size; this minimizes the number of calls to realloc, which is potentially expensive. Of course that means you'll have to keep track of two quantities; the total size of the array and the number of elements currently read.

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

Example:

例子:

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

回答by Ed Heal

If you are using C you will need to implement the resizing of the array yourself. C++ and the SDL has this done for you. It is called a vector. http://www.cplusplus.com/reference/stl/vector/

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