我试图从 c 中的文件中读取一行并动态分配内存,但结果总是不好
I'm trying to read a line from a file in c and dynamically allocate memory but the result is always bad
char * readline(FILE *fp, char *buffer) {
char ch;
int i = 0, buff_len = 0;
buffer = malloc(buff_len);
while ((ch = fgetc(fp)) != '\n' && ch != EOF) {
++buff_len;
buffer = realloc(buffer, buff_len);
buffer[i] = ch;
++i;
}
return buffer;
}
我正在尝试读取一个字符后直接分配内存。
代码从不通过附加空字符使分配的缓冲区成为字符串,也不会为其分配 space。
char * readline(FILE *fp, char *buffer) {
int ch;
int i = 0;
size_t buff_len = 0;
buffer = malloc(buff_len + 1);
if (!buffer) return NULL; // Out of memory
while ((ch = fgetc(fp)) != '\n' && ch != EOF) {
buff_len++;
void *tmp = realloc(buffer, buff_len + 1);
if (tmp == NULL) {
free(buffer);
return NULL; // Out of memory
}
buffer = tmp;
buffer[i] = (char) ch;
i++;
}
buffer[i] = '[=10=]';
// Detect end
if (ch == EOF && (i == 0 || ferror(fp))) {
free(buffer);
return NULL;
}
return buffer;
}
注:
在每次迭代中重新分配有点浪费。
buffer
的输入值从未使用过。也许重新设计OP的功能。
ch
应该是 int
以正确区分 char from
EOF`。
如 chux 所述,建议的替代方案。显示文本文件的示例代码。
#include <stdio.h>
#include <stdlib.h>
char * readline(FILE *fp) {
char * buffer = malloc(1024); /* assume longest line < 1023 chars */
int ch;
int i = 0;
while ((ch = fgetc(fp)) != '\n' && ch != EOF)
buffer[i++] = ch;
if(ch == EOF){ /* if eof, free buffer, return */
free(buffer);
return 0;
}
buffer[i++] = 0; /* add 0 terminator */
buffer = realloc(buffer, i);
return buffer;
}
int main(int argc, char *argv[])
{
FILE *fp;
char *pline;
if(argc < 2)
return 0;
fp = fopen(argv[1], "r");
while(1){
pline = readline(fp);
if(pline == 0)
break;
printf("%s\n", pline);
free(pline);
}
fclose(fp);
return 0;
}
将指针传递给函数以进行分配、填充和 return 有几个微妙之处。最重要的是要理解,当将指针传递给函数时,函数会收到该指针的 copy 以及一个新的单独地址。如果随后在函数中为指针分配 space,则 必须 return 指向调用者的指针地址(此处为 main()
)或调用者将无法访问您存储在新分配的内存块中的值。
要克服这个问题,并能够传递一个指向函数的指针进行分配和填充,而 不必使用 return,您必须通过函数指针的地址,即:
char *readline (FILE *fp, char **buffer)
否则,正如评论和上面所讨论的,没有理由将指向缓冲区的指针传递给函数。您可以简单地声明 buffer
局部函数,为其动态分配 space,并将 return 起始地址分配给新的内存块。
无论哪种方式都没有错,它实际上归结为您需要什么,但您想要清楚传递给函数的内容以及原因。下面是一个简短的示例,将指针的地址传递给 readline
并允许函数在每一行中 allocate/fill 而无需使用 return。现在,在这种情况下,return 指向您的行的指针永远不会受到伤害。它提供了一种确定 success/failure 的方法,并且可以根据需要灵活地分配 return:
#include <stdio.h>
#include <stdlib.h>
#define NCHAR 64
char *readline (FILE *fp, char **buffer);
int main (int argc, char **argv) {
char *line = NULL;
size_t idx = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (readline (fp, &line)) { /* read each line in 'fp' */
printf (" line[%2zu] : %s\n", idx++, line);
free (line);
line = NULL;
}
if (fp != stdin) fclose (fp);
return 0;
}
/* read line from 'fp' allocate *buffer NCHAR in size
* realloc as necessary. Returns a pointer to *buffer
* on success, NULL otherwise.
*/
char *readline (FILE *fp, char **buffer)
{
int ch;
size_t buflen = 0, nchar = NCHAR;
*buffer = malloc (nchar); /* allocate buffer nchar in length */
if (!*buffer) {
fprintf (stderr, "readline() error: virtual memory exhausted.\n");
return NULL;
}
while ((ch = fgetc(fp)) != '\n' && ch != EOF)
{
(*buffer)[buflen++] = ch;
if (buflen + 1 >= nchar) { /* realloc */
char *tmp = realloc (*buffer, nchar * 2);
if (!tmp) {
fprintf (stderr, "error: realloc failed, "
"returning partial buffer.\n");
(*buffer)[buflen] = 0;
return *buffer;
}
*buffer = tmp;
nchar *= 2;
}
}
(*buffer)[buflen] = 0; /* nul-terminate */
if (buflen == 0 && ch == EOF) { /* return NULL if nothing read */
free (*buffer);
*buffer = NULL;
}
return *buffer;
}
输入
$ cat ../dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
输出
$ ./bin/readline ../dat/captnjack.txt
line[ 0] : This is a tale
line[ 1] : Of Captain Jack Sparrow
line[ 2] : A Pirate So Brave
line[ 3] : On the Seven Seas.
备注
你真的不想为每个角色分配。 malloc
是一个相对昂贵的操作。为每一行分配一些合理预期的字符数,如果达到该限制,然后 realloc
比为每个字符分配 realloc
更有意义。如果您关心的只是分配恰好容纳字符串所需的数量,则按照描述进行分配,并在末尾为 strlen(buf) + 1
个字符执行一个最终的 realloc
。
此外,以这种方式分配,您可以随时设置 #define NCHAR 1
并强制开始分配,如果您愿意,还可以设置 1-char
。
内存Leak/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向内存块起始地址的指针,因此,(2) 它可以是不再需要时释放。您必须使用内存错误检查程序来确保您没有写入 beyond/outside 您分配的内存块并确认您已释放所有分配的内存。对于 Linux valgrind
是正常的选择。滥用内存块的微妙方法有很多,可能会导致真正的问题,没有理由不这样做。每个平台都有类似的内存检查器。它们都易于使用。只是 运行 你的程序通过它。
$ valgrind ./bin/readline ../dat/captnjack.txt
==16460== Memcheck, a memory error detector
==16460== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==16460== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==16460== Command: ./bin/readline ../dat/captnjack.txt
==16460==
line[ 0] : This is a tale
line[ 1] : Of Captain Jack Sparrow
line[ 2] : A Pirate So Brave
line[ 3] : On the Seven Seas.
==16460==
==16460== HEAP SUMMARY:
==16460== in use at exit: 0 bytes in 0 blocks
==16460== total heap usage: 6 allocs, 6 frees, 888 bytes allocated
==16460==
==16460== All heap blocks were freed -- no leaks are possible
==16460==
==16460== For counts of detected and suppressed errors, rerun with: -v
==16460== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
你应该每次都能看到 All heap blocks were freed -- no leaks are possible
和 ERROR SUMMARY: 0 errors from 0 contexts
。
注意: 已更新以反映对 return 的评论并键入 ch
并修复 returning [= 时潜在的内存泄漏29=] 来自 readline
EOF
。
char * readline(FILE *fp, char *buffer) {
char ch;
int i = 0, buff_len = 0;
buffer = malloc(buff_len);
while ((ch = fgetc(fp)) != '\n' && ch != EOF) {
++buff_len;
buffer = realloc(buffer, buff_len);
buffer[i] = ch;
++i;
}
return buffer;
}
我正在尝试读取一个字符后直接分配内存。
代码从不通过附加空字符使分配的缓冲区成为字符串,也不会为其分配 space。
char * readline(FILE *fp, char *buffer) {
int ch;
int i = 0;
size_t buff_len = 0;
buffer = malloc(buff_len + 1);
if (!buffer) return NULL; // Out of memory
while ((ch = fgetc(fp)) != '\n' && ch != EOF) {
buff_len++;
void *tmp = realloc(buffer, buff_len + 1);
if (tmp == NULL) {
free(buffer);
return NULL; // Out of memory
}
buffer = tmp;
buffer[i] = (char) ch;
i++;
}
buffer[i] = '[=10=]';
// Detect end
if (ch == EOF && (i == 0 || ferror(fp))) {
free(buffer);
return NULL;
}
return buffer;
}
注:
在每次迭代中重新分配有点浪费。
buffer
的输入值从未使用过。也许重新设计OP的功能。
ch
应该是 int
以正确区分 char from
EOF`。
如 chux 所述,建议的替代方案。显示文本文件的示例代码。
#include <stdio.h>
#include <stdlib.h>
char * readline(FILE *fp) {
char * buffer = malloc(1024); /* assume longest line < 1023 chars */
int ch;
int i = 0;
while ((ch = fgetc(fp)) != '\n' && ch != EOF)
buffer[i++] = ch;
if(ch == EOF){ /* if eof, free buffer, return */
free(buffer);
return 0;
}
buffer[i++] = 0; /* add 0 terminator */
buffer = realloc(buffer, i);
return buffer;
}
int main(int argc, char *argv[])
{
FILE *fp;
char *pline;
if(argc < 2)
return 0;
fp = fopen(argv[1], "r");
while(1){
pline = readline(fp);
if(pline == 0)
break;
printf("%s\n", pline);
free(pline);
}
fclose(fp);
return 0;
}
将指针传递给函数以进行分配、填充和 return 有几个微妙之处。最重要的是要理解,当将指针传递给函数时,函数会收到该指针的 copy 以及一个新的单独地址。如果随后在函数中为指针分配 space,则 必须 return 指向调用者的指针地址(此处为 main()
)或调用者将无法访问您存储在新分配的内存块中的值。
要克服这个问题,并能够传递一个指向函数的指针进行分配和填充,而 不必使用 return,您必须通过函数指针的地址,即:
char *readline (FILE *fp, char **buffer)
否则,正如评论和上面所讨论的,没有理由将指向缓冲区的指针传递给函数。您可以简单地声明 buffer
局部函数,为其动态分配 space,并将 return 起始地址分配给新的内存块。
无论哪种方式都没有错,它实际上归结为您需要什么,但您想要清楚传递给函数的内容以及原因。下面是一个简短的示例,将指针的地址传递给 readline
并允许函数在每一行中 allocate/fill 而无需使用 return。现在,在这种情况下,return 指向您的行的指针永远不会受到伤害。它提供了一种确定 success/failure 的方法,并且可以根据需要灵活地分配 return:
#include <stdio.h>
#include <stdlib.h>
#define NCHAR 64
char *readline (FILE *fp, char **buffer);
int main (int argc, char **argv) {
char *line = NULL;
size_t idx = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (readline (fp, &line)) { /* read each line in 'fp' */
printf (" line[%2zu] : %s\n", idx++, line);
free (line);
line = NULL;
}
if (fp != stdin) fclose (fp);
return 0;
}
/* read line from 'fp' allocate *buffer NCHAR in size
* realloc as necessary. Returns a pointer to *buffer
* on success, NULL otherwise.
*/
char *readline (FILE *fp, char **buffer)
{
int ch;
size_t buflen = 0, nchar = NCHAR;
*buffer = malloc (nchar); /* allocate buffer nchar in length */
if (!*buffer) {
fprintf (stderr, "readline() error: virtual memory exhausted.\n");
return NULL;
}
while ((ch = fgetc(fp)) != '\n' && ch != EOF)
{
(*buffer)[buflen++] = ch;
if (buflen + 1 >= nchar) { /* realloc */
char *tmp = realloc (*buffer, nchar * 2);
if (!tmp) {
fprintf (stderr, "error: realloc failed, "
"returning partial buffer.\n");
(*buffer)[buflen] = 0;
return *buffer;
}
*buffer = tmp;
nchar *= 2;
}
}
(*buffer)[buflen] = 0; /* nul-terminate */
if (buflen == 0 && ch == EOF) { /* return NULL if nothing read */
free (*buffer);
*buffer = NULL;
}
return *buffer;
}
输入
$ cat ../dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
输出
$ ./bin/readline ../dat/captnjack.txt
line[ 0] : This is a tale
line[ 1] : Of Captain Jack Sparrow
line[ 2] : A Pirate So Brave
line[ 3] : On the Seven Seas.
备注
你真的不想为每个角色分配。 malloc
是一个相对昂贵的操作。为每一行分配一些合理预期的字符数,如果达到该限制,然后 realloc
比为每个字符分配 realloc
更有意义。如果您关心的只是分配恰好容纳字符串所需的数量,则按照描述进行分配,并在末尾为 strlen(buf) + 1
个字符执行一个最终的 realloc
。
此外,以这种方式分配,您可以随时设置 #define NCHAR 1
并强制开始分配,如果您愿意,还可以设置 1-char
。
内存Leak/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向内存块起始地址的指针,因此,(2) 它可以是不再需要时释放。您必须使用内存错误检查程序来确保您没有写入 beyond/outside 您分配的内存块并确认您已释放所有分配的内存。对于 Linux valgrind
是正常的选择。滥用内存块的微妙方法有很多,可能会导致真正的问题,没有理由不这样做。每个平台都有类似的内存检查器。它们都易于使用。只是 运行 你的程序通过它。
$ valgrind ./bin/readline ../dat/captnjack.txt
==16460== Memcheck, a memory error detector
==16460== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==16460== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==16460== Command: ./bin/readline ../dat/captnjack.txt
==16460==
line[ 0] : This is a tale
line[ 1] : Of Captain Jack Sparrow
line[ 2] : A Pirate So Brave
line[ 3] : On the Seven Seas.
==16460==
==16460== HEAP SUMMARY:
==16460== in use at exit: 0 bytes in 0 blocks
==16460== total heap usage: 6 allocs, 6 frees, 888 bytes allocated
==16460==
==16460== All heap blocks were freed -- no leaks are possible
==16460==
==16460== For counts of detected and suppressed errors, rerun with: -v
==16460== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
你应该每次都能看到 All heap blocks were freed -- no leaks are possible
和 ERROR SUMMARY: 0 errors from 0 contexts
。
注意: 已更新以反映对 return 的评论并键入 ch
并修复 returning [= 时潜在的内存泄漏29=] 来自 readline
EOF
。