虽然链接的示例比您在此处所需的要复杂得多,但原理是相同的。当您声明一个函数void 并消除返回指向新分配内存块的指针的能力时,您需要传递您需要为其分配存储空间的指针的地址。 p>
为什么是的地址?
当您最初分配或重新分配时,内存块的地址将会改变。如果您只是将一个指针传递给您的函数以进行分配,则该函数会收到原始指针的 副本,该指针具有其自己的且非常不同的地址。在 C 中,所有参数都按值传递,并且函数接收一个副本。对函数中的副本所做的任何更改都会在函数返回时丢失。
但是,如果您传递变量的地址(指针间接一级的附加级别),该函数会收到指向您的变量的指针的副本,但该指针保存原始地址指针。因此,现在您可以进行更改并将新的内存块分配给函数中取消引用的指针,这会将内存块的新起始地址分配给原始指针,并且更改将在调用函数中看到。
在您的情况下,您想从文件中读取未知数量的字符串,并将它们添加到 void 函数中分配的字符串集合。如果您只是简单地在main() 中分配/重新分配,您可以简单地声明一个char **ptrs;(双指针),然后分配/重新分配您从文件中读取的每个字符串所拥有的指针数量,为字符串,并将块的地址分配给您新分配的指针,然后将字符串复制到您的指针 - 并重复直到用完字符串。
在void 函数中分配和复制时唯一改变的是您将char **ptrs; 的地址传递给添加一级指针间接的函数(因此函数参数类型将为char ***,然后在函数中你必须删除一个额外的间接级别来操作指针.你还需要传递一个变量的地址来保存当前分配的指针数量——所以你知道你需要多少@987654329 @函数内。
总而言之,你的函数可以写成:
/* allocate pointer for *strings, allocate/copy s to (*strings)[*nptr] */
void allocmem (char ***strings, size_t *nptrs, const char *s)
{
size_t len = strlen (s); /* determine length of s */
/* reallocate *string to a temporary pointer, adding 1 pointer */
void *tmp = realloc (*strings, (*nptrs + 1) * sizeof **strings);
if (!tmp) { /* validate EVERY allocation/reallocation */
perror ("realloc-allocptr-strings");
exit (EXIT_FAILURE);
}
*strings = tmp; /* assign newly allocated block of pointers to *strings */
(*strings)[*nptrs] = malloc (len + 1); /* allocate storage for s */
if (!(*strings)[*nptrs]) { /* ditto */
perror ("malloc-(*strings)[*nptrs]");
exit (EXIT_FAILURE);
}
memcpy ((*strings)[*nptrs], s, len + 1); /* copy s to allocated storage */
*nptrs += 1; /* increment pointer count after successful allocation/copy */
}
(注意:分配失败时,程序简单地退出——但对于实际代码,您需要一些方法来知道是否发生了两个分配以及字符串是否被复制。保存之前/之后nptrs 的计数可以提供部分图片,但无法告诉您哪个分配失败 - 如果 realloc() 失败,那么由于您使用了指向 realloc() 的 tmp 指针,因此您的指针和字符串就像它们一样在函数调用被保留之前存在并且仍然可以通过原始指针访问)
(note2:(*strings)[*nptrs] 中*strings 周围的括号是必需的,因为在C Operator Precedence 中,[..] 的优先级高于取消引用运算符'*')
把一个例子放在一起来读取你的文件和存储字符串是你分配的指针和字符串的集合,你可以这样做:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
/* allocate pointer for *strings, allocate/copy s to (*strings)[*nptr] */
void allocmem (char ***strings, size_t *nptrs, const char *s)
{
size_t len = strlen (s); /* determine length of s */
/* reallocate *string to a temporary pointer, adding 1 pointer */
void *tmp = realloc (*strings, (*nptrs + 1) * sizeof **strings);
if (!tmp) { /* validate EVERY allocation/reallocation */
perror ("realloc-allocptr-strings");
exit (EXIT_FAILURE);
}
*strings = tmp; /* assign newly allocated block of pointers to *strings */
(*strings)[*nptrs] = malloc (len + 1); /* allocate storage for s */
if (!(*strings)[*nptrs]) { /* ditto */
perror ("malloc-(*strings)[*nptrs]");
exit (EXIT_FAILURE);
}
memcpy ((*strings)[*nptrs], s, len + 1); /* copy s to allocated storage */
*nptrs += 1; /* increment pointer count after successful allocation/copy */
}
int main (int argc, char **argv) {
char buf[MAXC], /* temp storage for each line read from file */
**strings = NULL; /* must initialize pointer NULL */
size_t nptrs = 0; /* number of pointers allocated */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
buf[strcspn (buf, "\n")] = 0; /* trim \n from end of buf */
allocmem (&strings, &nptrs, buf); /* add string to strings */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < nptrs; i++) { /* output all stored strings */
printf ("strings[%zu] : %s\n", i, strings[i]);
free (strings[i]); /* free storage for string */
}
free (strings); /* free pointers */
}
使用/输出示例
$ ./bin/3starstrings dat/test1-7.txt
strings[0] : first
strings[1] : second
strings[2] : third
strings[3] : fourth
strings[4] : fifth
strings[5] : sixth
strings[6] : seventh
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此 (2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。
对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。
$ valgrind ./bin/3starstrings dat/test1-7.txt
==23799== Memcheck, a memory error detector
==23799== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==23799== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==23799== Command: ./bin/3starstrings dat/test1-7.txt
==23799==
strings[0] : first
strings[1] : second
strings[2] : third
strings[3] : fourth
strings[4] : fifth
strings[5] : sixth
strings[6] : seventh
==23799==
==23799== HEAP SUMMARY:
==23799== in use at exit: 0 bytes in 0 blocks
==23799== total heap usage: 17 allocs, 17 frees, 5,942 bytes allocated
==23799==
==23799== All heap blocks were freed -- no leaks are possible
==23799==
==23799== For counts of detected and suppressed errors, rerun with: -v
==23799== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放已分配的所有内存并且没有内存错误。
如果您还有其他问题,请查看并告诉我(请记住,被称为 3 星程序员 并不是恭维 - 从您的函数返回一个指针而不是 :)