我正在创建一个返回字符串的函数。字符串的大小在运行时是已知的,因此我计划使用malloc()
,但我不想让用户在使用我的函数后让用户负责调用free()
#39; s返回值。
如何实现这一目标?返回字符串(char *
)的其他函数如何工作(例如getcwd()
,_getcwd()
,GetLastError()
,SDL_GetError()
)?
答案 0 :(得分:5)
您的挑战是某些需要释放资源(即导致free()
发生)。
通常,调用者可以通过直接调用free()
来释放分配的内存(例如,查看strdup
用户如何工作),或者通过调用您提供换行free
的函数来释放已分配的内存。例如,您可能需要呼叫者呼叫foo_destroy
功能。正如另一张海报所指出的,可能选择将其包装在不透明的struct
中,尽管这不是必需因为拥有自己的分配和销毁功能即使没有(例如,用于资源跟踪)。
然而,另一种方法是使用某种形式的清理功能。例如,在分配字符串时,您可以将其附加到池中分配的资源列表,然后在完成后立即释放池。这就是apache2
与apr_pool
结构一起使用的方式。一般情况下,您不会free()
具体在该模型下的任何内容。请参阅here和(更易于阅读)here。
你不能在C中做什么(因为没有malloc()
d结构的引用计数)直接决定了对象的最后一个'引用'何时超出范围然后释放它。那是因为你没有引用,你有指针。
最后,您询问现有函数如何返回char *
个变量:
有些(在某些情况下,如strdup
,get_current_dir_name
和getcwd
)希望来电者可以免费使用。
有些(在其他情况下,如strerror_r
和getcwd
)期望调用者传入足够大小的缓冲区。
有些人都这样做:来自getcwd
手册页:
作为POSIX.1-2001标准的扩展,Linux(libc4,libc5,glibc)
getcwd()
动态分配缓冲区 如果malloc(3)
为buf
,请使用NULL
。在这种情况下,分配的缓冲区长度为size
,除非size
为零,何时为buf
根据需要分配。调用者应该free(3)
返回的缓冲区。
有些人使用内部静态缓冲区,因此不是可重入/线程安全的(哎呀 - 不要这样做)。请参阅strerror
以及发明strerror_r
的原因。
有些只返回指向常量的指针(所以重入是好的),并且不需要空闲。
有些(例如libxml
)要求您使用单独的free
函数(在这种情况下为xmlFree()
)
有些(如apr_palloc
)依赖于上面的池技术。
答案 1 :(得分:1)
无法在C 中执行 。您必须传递带有大小信息的参数,以便可以在被调用函数中调用malloc()
和free()
,或者调用函数必须在malloc()
之后调用。
许多面向对象的语言(例如C ++)以执行您想要的方式处理内存,而不是C.
修改
通过大小 信息作为参数,我的意思是让被调用的函数知道您传递的指针拥有多少字节的内存。这可以通过直接查看被调用的字符串来完成,如果已经为其分配了值,例如:
char test1[]="this is a test";
char *test2="this is a test";
当这样调用时:
readString(test1); // (or test2)
char * readString(char *abc)
{
int len = strlen(abc);
return abc;
}
这两个参数都会导致len
= 14
然而 如果您创建一个非填充变量,例如:
char *test3;
并分配相同数量的内存,但不要填充它,例如:
test3 = malloc(strlen("this is a test") +1);
被调用函数无法知道已分配的内存。 len
的第一个原型中的变量readString()
将== 0。但是,如果您将原型readString()
更改为:
readString(char *abc, int sizeString); Then size information as an argument can be used to create memory:
void readString(char *abc, size_t sizeString)
{
char *in;
in = malloc(sizeString +1);
//do something with it
//then free it
free(in);
}
示例电话:
int main()
{
int len;
char *test3;
len = strlen("this is a test") +1; //allow for '\0'
readString(test3, len);
// more code
return 0;
}
答案 2 :(得分:1)
许多库强制用户处理内存分配。这是个好主意,因为每个应用程序都有自己的对象生存和重用模式。图书馆尽可能少地假设其用户是很好的。
假设用户想要像这样调用您的库函数:
for (a lot of iterations)
{
params = get_totally_different_params();
char *str = your_function(params);
do_something(str);
// now we're done with this str forever
}
如果您的libary malloc
每次都是字符串,那么调用malloc
会浪费很多精力,并且如果malloc
每次选择不同的块,可能会显示缓存行为不佳。
根据您图书馆的具体情况,您可能会这样做:
int output_size(/*params*/);
void func(/*params*/, char *destination);
其中destination
的大小至少为output_size(params)
,或者您可以执行类似套接字recv
API的操作:
int func(/*params*/, char *destination, int destination_size);
返回值为:
< desination_size: this is the number of bytes we actually used
== destination_size: there may be more bytes waiting to output
这些模式在重复调用时都表现良好,因为调用者可以反复重复使用相同的内存块而无需任何分配。
答案 3 :(得分:0)
你无法在C中执行此操作。
返回一个指针,由调用该函数的人调用free
或者使用C ++。 shared_ptr
等
答案 4 :(得分:0)
您可以将其包装在不透明的结构中。
允许用户访问指向结构的指针,但不能访问其内部指针。创建一个释放资源的功能。
void release_resources(struct opaque *ptr);
当然用户需要调用该函数。
答案 5 :(得分:0)
您可以跟踪分配的字符串并在atexit例程(http://www.tutorialspoint.com/c_standard_library/c_function_atexit.htm)中释放它们。在下面,我使用了一个全局变量,但如果你有一个方便的话,它可以是一个简单的数组或列表。
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
char* freeme = NULL;
void AStringRelease(void)
{
if (freeme != NULL)
free(freeme);
}
char* AStringGet(void)
{
freeme = malloc(20);
strcpy(result, "A String");
atexit(AStringRelease);
return freeme;
}