Sup*_*can 1 c arrays fgets getline multidimensional-array
我有以下可以动态分配单个句子的代码:
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);
Run Code Online (Sandbox Code Playgroud)
我希望能够分配和存储以 '\n' 结尾的多行以进一步使用(格式),我不知道我将分配多少行或它们有多长。行的输入以EOF 结束。它不必与 fgets 一起使用。例如:
有任何想法吗?
这是如何处理动态分配和重新分配以存储未知数量的字符串的经典问题。值得详细了解这个过程,因为它将作为您读取未知数量的值(无论它们是结构体、浮点数、字符等)的任何其他情况的基础。
您可以使用多种不同类型的数据结构,例如列表、树等,但基本方式(如您所说的“2D char 数组” )是通过创建指针到指针的数组来处理的。-type(本例中为typechar),然后分配空间,填充数据,并在读取数据时将新内存块的起始地址分配给每个指针。指针到指针到类型的简写是简单的双指针(例如char **array;,从技术上讲,它是指针到指针到字符或指针到字符*,如果您愿意的话)
为未知行数分配内存的通用且有效的方法是首先分配合理预期数量的指针(每个预期行 1 个)。realloc这比为您读取的每一行调用和重新分配整个集合要高效得多。在这里,您只需保留一个读取行数的计数器,当达到原始分配限制时,您只需重新分配当前拥有的指针数量的两倍即可。请注意,您可以自由添加您选择的任何增量金额。您可以每次简单地添加固定金额,也可以使用原始金额的一些缩放倍数 - 这取决于您。重新分配两倍电流只是标准方案之一。
最初分配指针时,作为重新分配的一部分,您可以通过将每个指针设置为 来受益NULL。对于原始分配来说,这很容易完成。只需使用calloc而不是malloc. 在重新分配时,它要求您设置分配给 的所有新指针NULL。
为什么?这不是强制性的,但这样做可以让您在不知道行数的情况下迭代指针数组。这是如何运作的?例如,假设您初始化了 100 个指针,NULL并为每个指针分配了一些行。要迭代集合,您可以简单地执行以下操作:
size_t i = 0;
while (array[i]) {
... do your stuff ...
}
Run Code Online (Sandbox Code Playgroud)
只有您分配了某些内容的指针才会有值。因此,循环只会对具有值的指针进行交互,并在NULL遇到第一个指针时停止。(第一个NULL只是作为您的哨兵值,告诉您何时停止)。这还提供了将指向集合的指针传递给任何函数的能力,而无需传递所包含的行/值的数量。(注意:没有理由不传递集合的大小,但在某些情况下这是一个好处)
下面的示例使用传统方法,迭代固定数量的行来打印行,然后释放分配的内存,但没有理由不能在这两种情况下简单地迭代有效指针来完成相同的事情。
为线路分配存储空间时也是如此。如果使用calloc代替malloc,则将所有值初始化为0( nul)。然后,通过初始化,所有字符串的存储都保证以null 结尾。这同样适用于数值数组的分配。通过初始化所有值,可以0防止意外尝试读取未初始化值(未定义的行为)的可能性。虽然当您顺序填充/读取数组时通常不是问题,但当使用随机存储和检索例程时,这可能是一个真正的问题。
分配内存时,您必须验证每个调用是否成功(例如malloc,对于calloc、realloc、 以及为您分配的其他函数调用,例如strdup)。这只是一个简单的检查,但需要养成每次都执行的习惯,否则会冒尝试从未分配的内存进行读写的风险。在下面的示例中,简单的函数用作 和 的包装器calloc,realloc提供必要的检查。虽然不需要使用类似的辅助函数,但它们有助于使代码主体免于重复的内存检查等,这些检查会使代码更难以阅读。
最后一点关于realloc. 始终使用临时变量来保存 的返回值realloc。为什么?成功时realloc返回指向新分配的内存块的指针。失败时返回NULL。如果您未能使用临时指针并且请求失败,您将失去对所有先前存储的值的访问(丢失其地址)。一旦验证realloc成功,只需将临时指针分配给原始指针即可。
下面的示例将从作为程序的第一个参数(或stdin默认情况下)给出的文件名中读取所有行。它用于fgets读取每一行。测试每一行以确保成功读取该行中的所有字符。如果该行太长,无法容纳所提供的空间,则会给出一个简单的警告,并将剩余部分读入下一行存储中(您realloc也可以在此处连接)。所有行都存储在array. 最初分配了MAXL(64)个指针,每行可以容纳MAXC(256)个字符。您可以进行更改以满足您的需要,也可以设置MAXL为1强制从此处开始重新分配线路。这些行只是打印到终端,然后在程序退出之前释放所有内存。
#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;
}
Run Code Online (Sandbox Code Playgroud)
编译
size_t i = 0;
while (array[i]) {
... do your stuff ...
}
Run Code Online (Sandbox Code Playgroud)
使用/输出
#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;
}
Run Code Online (Sandbox Code Playgroud)
内存泄漏/错误检查
在您编写的动态分配内存的任何代码中,对于分配的任何内存块,您都有 2 个责任:(1) 始终保留指向内存块起始地址的指针,因此,(2) 当内存块不存在时可以将其释放。更需要。您必须使用内存错误检查程序来确保您没有在分配的内存块之外进行写入,并确认您已释放已分配的所有内存。对于Linux来说valgrind是正常的选择。有很多微妙的方法可以滥用内存块,从而导致真正的问题,没有理由不这样做。每个平台都有类似的内存检查器。它们都很简单易用。只需通过它运行您的程序即可。
gcc -Wall -Wextra -O3 -o bin/fgets_lines_dyn fgets_lines_dyn.c
Run Code Online (Sandbox Code Playgroud)
只需查找All heap block were freed - noleaks are possible和ERROR Summary: 0Errors from 0 context。如果两者都没有,请返回并找出原因。
虽然这个过程比预想的要长得多,但这是值得理解的。如果您还有其他疑问,请告诉我。
| 归档时间: |
|
| 查看次数: |
2843 次 |
| 最近记录: |