使用malloc的动态2D字符数组
我有以下代码可以动态分配一个句子:使用malloc的动态2D字符数组,c,arrays,multidimensional-array,fgets,getline,C,Arrays,Multidimensional Array,Fgets,Getline,我有以下代码可以动态分配一个句子: int size=1; char * text = (char*) malloc(size * sizeof(char)); size = (int)sizeof(text); fgets(text, si, stdin); //remove new line() printf ("Sentence = <%s>\n", text); int size=1; char*text=(char*)malloc(size*sizeof(cha
int size=1;
char * text = (char*) malloc(size * sizeof(char));
size = (int)sizeof(text);
fgets(text, si, stdin);
//remove new line()
printf ("Sentence = <%s>\n", text);
int size=1;
char*text=(char*)malloc(size*sizeof(char));
size=(int)sizeof(text);
fgets(文本、si、标准文本);
//删除新行()
printf(“句子=\n”,文本);
我希望能够分配和存储多个以“\n”结尾的行以进一步使用(格式),我不知道将分配多少行或它们的长度。行的输入以EOF结束。它不一定要与fgets在一起。
例如:
- 句子1\n
- 句子2\n ……等等
有什么想法吗?这是一个经典问题,即如何处理动态分配和重新分配以存储未知数量的字符串。值得详细了解此过程,因为它将作为任何其他情况的基础,在这些情况下,您将读取未知数量的值(无论它们是结构、浮点、字符等) 您可以使用多种不同类型的数据结构,如列表、树等,但基本方法(您称之为“2D char数组”)是通过创建指向类型的指针数组(在本例中,类型为
char
)然后分配空间,填充数据,以及在读取数据时为每个指针分配新内存块的起始地址。指向类型的指针的缩写是双指针(例如,char**array;
,从技术上讲,它是指向char的指针或指向char*的指针,如果您愿意的话)
为未知数量的行分配内存的一般且有效的方法是首先分配合理预期数量的指针(每个预期行1个)。这比调用realloc
并为您阅读的每一行重新分配整个集合要高效得多。在这里,您只需保留一个读取行数计数器,当您达到原始分配限制时,您可以简单地重新分配当前指针数的两倍。注意,您可以自由添加您选择的任何增量金额。你可以简单地每次添加一个固定的数量,或者你可以使用一些原始的倍数——这取决于你自己。两倍电流的realloc只是标准方案之一
最初分配指针时,作为重新分配的一部分,您可以将每个指针设置为NULL
。对于原始分配,这很容易实现。只需使用calloc
而不是malloc
。重新分配时,需要将分配给NULL
的所有新指针设置为空
为什么??这不是强制性的,但这样做可以让您在不知道行数的情况下迭代指针数组。这是怎么回事?例如,假设您初始化了100个指向NULL
的指针,并在运行时为每个指针分配了许多行。要迭代集合,只需执行以下操作:
size_t i = 0;
while (array[i]) {
... do your stuff ...
}
只有分配了内容的指针才会有值。因此,循环将只在具有值的指针上进行交互,在遇到第一个NULL
指针时停止。(第一个NULL
只是作为您的哨兵值,告诉您何时停止)。这还提供了将指向集合的指针传递给任何函数的能力,而无需传递包含的行数/值。(注意:没有理由不通过集合的大小,但在某些情况下,这是一种好处)
下面的示例使用了传统的方法,即迭代固定数量的行来打印行,然后释放分配的内存,但是没有理由不能在这两种情况下简单地迭代有效指针来完成相同的任务
在为线路分配存储空间时也是如此。如果使用calloc
而不是malloc
,则将所有值初始化为0
(nul
)。然后,通过初始化保证所有字符串的存储都以nul终止。这同样适用于数字数组的分配。通过将所有值初始化为0
,可以防止意外尝试读取未初始化值(未定义行为)的可能性。当您按顺序填充/读取数组时,这通常不是问题,但当使用随机存储和检索例程时,这可能是一个真正的问题
分配内存时,必须验证每个调用是否成功(例如,对于malloc
,calloc
,realloc
,以及其他为您分配的函数调用,如strdup
)。这只是一个简单的检查,但是每次尝试从未分配的内存读写时都要养成这样做的习惯,否则会有风险。在下面的示例中,简单函数用作提供必要检查的calloc
和realloc
的包装器。虽然不需要使用类似的帮助函数,但它们有助于保持代码的主体不受重复的内存检查等影响,从而使代码更难阅读
关于realloc
的最后一个注释。始终使用临时变量保存realloc
的返回。为什么?成功时,realloc
返回指向新分配内存块的指针。失败时,它返回NULL
。如果无法使用临时指针且请求失败,则表示您无法访问(丢失了的地址)以前存储的所有值。验证realloc
成功后,只需将临时指针指定给原始指针即可
下面的示例将读取作为程序第一个参数的文件名中的所有行(或默认情况下stdin
)。它使用fgets
读取每一行。对每条线路进行测试,以确保所有ch
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 256 /* max chars per-line */
#define MAXL 64 /* initial num lines */
void *xcalloc (size_t n, size_t s);
void *xrealloc_dp (void *ptr, size_t *n);
int main (int argc, char **argv) {
char **array = NULL;
char buf[MAXC] = {0};
size_t i, idx = 0, maxl = MAXL;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
array = xcalloc (maxl, sizeof *array); /* allocate maxl pointers */
while (fgets (buf, MAXC, fp)) /* read all lines from fp into array */
{
size_t len = strlen (buf);
/* validate complete line read */
if (len + 1 == MAXC && buf[len - 1] != '\n')
fprintf (stderr, "warning: line[%zu] exceeded '%d' chars.\n",
idx, MAXC);
/* strip trailing '\r', '\n' */
while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
buf[--len] = 0;
/* allocate & copy buf to array[idx], nul-terminate
* note: this can all be done with array[idx++] = strdup (buf);
*/
array[idx] = xcalloc (len + 1, sizeof **array);
strncpy (array[idx], buf, len);
array[idx++][len] = 0;
/* realloc as required (note: maxl passed as pointer) */
if (idx == maxl) array = xrealloc_dp (array, &maxl);
}
if (fp != stdin) fclose (fp);
printf ("\n lines read from '%s'\n\n", argc > 1 ? argv[1] : "stdin");
for (i = 0; i < idx; i++)
printf (" line[%3zu] %s\n", i, array[i]);
for (i = 0; i < idx; i++)
free (array[i]); /* free each line */
free (array); /* free pointers */
return 0;
}
/* simple calloc with error checking */
void *xcalloc (size_t n, size_t s)
{
void *memptr = calloc (n, s);
if (memptr == 0) {
fprintf (stderr, "xcalloc() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
return memptr;
}
/* realloc array of pointers ('memptr') to twice current
* number of pointer ('*nptrs'). Note: 'nptrs' is a pointer
* to the current number so that its updated value is preserved.
* no pointer size is required as it is known (simply the size
* of a pointer
*/
void *xrealloc_dp (void *ptr, size_t *n)
{
void **p = ptr;
void *tmp = realloc (p, 2 * *n * sizeof tmp);
if (!tmp) {
fprintf (stderr, "xrealloc_dp() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
p = tmp;
memset (p + *n, 0, *n * sizeof tmp); /* set new pointers NULL */
*n *= 2;
return p;
}