I';我试图从c语言中的文件中读取一行并动态分配内存,但结果总是不好
我试图在读取一个字符后直接分配内存。代码从不通过添加空字符使分配的缓冲区成为字符串,也不为其分配空间I';我试图从c语言中的文件中读取一行并动态分配内存,但结果总是不好,c,file,memory,dynamic,allocation,C,File,Memory,Dynamic,Allocation,我试图在读取一个字符后直接分配内存。代码从不通过添加空字符使分配的缓冲区成为字符串,也不为其分配空间 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 = r
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;
}
注:在每次迭代中重新分配有点浪费。
从不使用
缓冲区的输入值。也许重新设计OP的功能。
ch
应为int
,以正确区分char和EOF`。如chux所述,建议的替代方案。显示文本文件的示例代码
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] = '\0';
// Detect end
if (ch == EOF && (i == 0 || ferror(fp))) {
free(buffer);
return NULL;
}
return buffer;
}
#包括
#包括
char*readline(文件*fp){
char*buffer=malloc(1024);/*假设最长行小于1023个字符*/
int-ch;
int i=0;
而((ch=fgetc(fp))!='\n'&&ch!=EOF)
缓冲区[i++]=ch;
if(ch==EOF){/*if-EOF,空闲缓冲区,返回*/
自由(缓冲);
返回0;
}
缓冲区[i++]=0;/*添加0终止符*/
缓冲区=realloc(缓冲区,i);
返回缓冲区;
}
int main(int argc,char*argv[])
{
文件*fp;
字符线;
如果(argc<2)
返回0;
fp=fopen(argv[1],“r”);
而(1){
pline=读线(fp);
如果(pline==0)
打破
printf(“%s\n”,pline);
免费(pline);
}
fclose(fp);
返回0;
}
将指针传递给要分配、填充和返回的函数有几个微妙之处。要理解的最重要的一点是,当向函数传递指针时,函数会收到该指针的一个副本,其中包含一个新的单独地址。如果随后在函数中为指针分配空间,则必须将指针的地址返回给调用者(main()
),否则调用者将无法访问存储在新分配的内存块中的值
为了克服此问题,并且能够传递指向函数的指针以进行分配和填充,而不必使用返回,您必须传递指向函数的指针地址,即:
#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;
}
否则,正如上面的注释所讨论的,没有理由将指向缓冲区的指针传递给函数。您只需将buffer
声明为函数的本地,为其动态分配空间,并将起始地址返回到新的内存块
无论采用哪种方式,都没有什么错,它实际上可以归结为您需要什么,但您希望清楚地了解您传递给函数的内容以及原因。下面是一个简短的示例,它将指针的地址传递给readline
,并允许函数分配/填充每一行,而无需使用返回。现在,在这种情况下,返回一个指向行的指针是不会有任何伤害的。它提供了一种确定成功/失败的方法,并提供了分配回报的灵活性(如果您愿意):
char *readline (FILE *fp, char **buffer)
输出
#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.
注意
您确实不希望为每个字符分配<代码>malloc
是一种相对昂贵的操作。为每行分配一些合理预期的字符数,然后在达到该限制时分配realloc
,比为每个字符分配realloc
更有意义。如果您只关心准确分配保存字符串所需的数量,则按照说明进行分配,并在末尾为strlen(buf)+1个字符执行最后一次realloc
此外,通过这种方式分配,您可以始终设置#定义NCHAR 1
,并强制开始分配和1-char
(如果愿意)
内存泄漏/错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有两个责任:(1)始终保留指向内存块起始地址的指针,以便(2)不再需要时可以释放它。您必须使用内存错误检查程序,以确保您没有在分配的内存块之外写入,并确认您已释放了分配的所有内存。对于Linux,valgrind
是正常的选择。有很多微妙的方法可以误用内存块,从而导致真正的问题,没有理由不这样做。每个平台都有类似的内存检查器。它们都很容易使用。只需运行程序即可
$ ./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.
您应该看到所有堆块都已释放--不可能发生泄漏
和错误摘要:每次都有0个上下文中的0个错误
注意:更新以反映对ch
的返回和键入的注释,并修复在EOF
上从readline
返回NULL
时可能出现的内存泄漏问题buffer
作为参数传递并立即覆盖它的原因是什么?为什么不把它变成一个局部变量呢?正确格式化代码。(文本下有一个edit
链接)。为什么不至少分配nchar=64
用于buffer
并保留一个charcount,当charcount+1==nchar
时,则realloc(buffer,nchar*2);nchar*=2代码>?好的,这绝对是我不希望我的CPU运行的代码类型…我也希望如此,但我从测试的角度考虑它,并解释一个空字符串的测试或简单的NULL
。它值得一张纸条。@chux-比纸条更值得-编辑和更改。但是当buflen==0
时,不管怎样,line
在main
中被释放,都没有内存泄漏。谢谢。非常感谢。关于if(ch==EOF){…*buffer=NULL
的提示。当文件中的最后一行不是以'\n'
结尾时,有多种方法。此代码返回NULL
并丢失最后的字符。类似fgets()
的功能
$ 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)