如何为二维数组分配和释放堆内存?
Posted
技术标签:
【中文标题】如何为二维数组分配和释放堆内存?【英文标题】:How to allocate and deallocate heap memory for 2D array? 【发布时间】:2011-11-05 12:35:11 【问题描述】:我习惯了 php,但我开始学习 C。我正在尝试创建一个程序,它可以逐行读取文件并将每一行存储到一个数组中。
到目前为止,我有一个程序可以逐行读取文件,甚至打印每一行,但现在我只需要将每一行添加到一个数组中。
昨晚我的朋友告诉了我一些关于这件事的事情。他说我必须在 C 中使用多维数组,所以基本上是array[x][y]
。 [y]
部分本身很简单,因为我知道每行的最大字节数。但是,我不知道该文件将有多少 行。
我想我可以让它循环遍历文件,每次只增加一个整数并使用它,但我觉得可能有更简单的方法。
有任何想法甚至是正确方向的提示吗?感谢您的帮助。
【问题讨论】:
以后可以使用realloc
函数来改变数组的大小。
我会去查找那个函数并尝试考虑如何实现它,我会回复你,谢谢
【参考方案1】:
您可以使用malloc
和realloc
函数动态分配和调整指向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]
,但实际上每一行只是它需要的长度。除了<err.h>
,它是 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);
当然,这仅适用于int
s 的数组。由于 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++;
当n
为LINE_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 已经为您完成了这项工作。它被称为vector
。 http://www.cplusplus.com/reference/stl/vector/
【讨论】:
以上是关于如何为二维数组分配和释放堆内存?的主要内容,如果未能解决你的问题,请参考以下文章
C 语言二级指针作为输入 ( 自定义二级指针内存 | 为 二级指针 分配内存 - 存放 一维指针 | 为每个 一级指针 分配内存 | 释放二维指针内存 )