将指针传递给函数进行分配,填充和返回时,存在一些微妙之处。要理解的最重要的一点是,当将指针传递给函数时,函数会接收到具有新的单独地址的该指针的副本。如果您随后在函数中为指针分配了空间,则必须将指针的地址返回给调用方(main()
此处),否则调用方将无法访问您新分配的内存块中存储的值。
为了克服此问题,并且能够在不利用返回值的情况下将指针传递给函数进行分配和填充,必须将指针的地址传递给函数,即:
char *readline (FILE *fp, char **buffer)
否则,如注释和以上所讨论,没有理由将指针传递给缓冲区到函数。您只需声明buffer
该函数的局部变量,为该函数动态分配空间,然后将起始地址返回到新的内存块即可。
无论哪种方式都没有错,它确实可以归结为您所需要的,但是您想要弄清楚要传递给函数的内容以及原因。下面是一个简短的示例,将指针的地址传递到该地址,readline
并允许该函数分配/填充每一行而无需利用返回值。现在,在这种情况下,将指针返回到您的行将毫不费力。它提供了一种确定成功/失败的方法,以及根据需要灵活分配收益的方法:
#include#include #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
为每个字符分配的字符数有意义得多。如果只考虑分配字符串所需的确切数量,请按照说明进行分配,最后realloc
对strlen(buf) + 1
字符进行最后一个处理。
此外,以这种方式进行分配,您始终可以根据需要设置#define NCHAR 1
并强制开始分配1-char
。
内存泄漏/错误检查
在您的任何动态分配内存的代码中,您对分配的任何内存块都有2种责任:(1)始终保留指向内存块起始地址的指针,因此,(2)在没有内存块的情况下可以将其释放需要更长的时间。必须使用一个内存错误检查程序来确保您没有在分配的内存块之外/之外进行写操作,并确认您已释放所有分配的内存。对于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
并修复NULL
从readline
on 返回时潜在的内存泄漏EOF
。
将指针传递给函数进行分配,填充和返回时,存在一些微妙之处。要理解的最重要的一点是,当将指针传递给函数时,函数会接收到具有新的单独地址的该指针的副本。如果您随后在函数中为指针分配了空间,则必须将指针的地址返回给调用方(main()
此处),否则调用方将无法访问您新分配的内存块中存储的值。
为了克服此问题,并且能够在不利用返回值的情况下将指针传递给函数进行分配和填充,必须将指针的地址传递给函数,即:
char *readline (FILE *fp, char **buffer)
否则,如注释和以上所讨论,没有理由将指针传递给缓冲区到函数。您只需声明buffer
该函数的局部变量,为该函数动态分配空间,然后将起始地址返回到新的内存块即可。
无论哪种方式都没有错,它确实可以归结为您所需要的,但是您想要弄清楚要传递给函数的内容以及原因。下面是一个简短的示例,将指针的地址传递到该地址,readline
并允许该函数分配/填充每一行而无需利用返回值。现在,在这种情况下,将指针返回到您的行将毫不费力。它提供了一种确定成功/失败的方法,以及根据需要灵活分配收益的方法:
#include#include #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
为每个字符分配的字符数有意义得多。如果只考虑分配字符串所需的确切数量,请按照说明进行分配,最后realloc
对strlen(buf) + 1
字符进行最后一个处理。
此外,以这种方式进行分配,您始终可以根据需要设置#define NCHAR 1
并强制开始分配1-char
。
内存泄漏/错误检查
在您的任何动态分配内存的代码中,您对分配的任何内存块都有2种责任:(1)始终保留指向内存块起始地址的指针,因此,(2)在没有内存块的情况下可以将其释放需要更长的时间。必须使用一个内存错误检查程序来确保您没有在分配的内存块之外/之外进行写操作,并确认您已释放所有分配的内存。对于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
并修复NULL
从readline
on 返回时潜在的内存泄漏EOF
。