[英]Returning allocated buffer, vs buffer passed to a function
将值传递给我的函数时,我经常考虑从 function 返回分配的缓冲区,而不是让 function 将缓冲区作为参数。 我试图弄清楚将缓冲区传递给我的 function 是否有任何重大好处(例如:
void f(char **buff) {
/* operations */
strcpy(*buff, value);
}
相对
char *f() {
char *buff = malloc(BUF_SIZE);
/* operations */
return buff;
}
这些显然不是超级高级的例子,但我认为这一点是正确的。 但是,是的,让用户传递分配的缓冲区有什么好处,还是返回分配的缓冲区更好?
使用其中一个有什么好处,还是只是没用?
这是 function 是否应通过其返回值或通过 out 参数将数据返回给其调用者的更一般问题的特定情况。 两种方法都可以正常工作,优缺点主要是风格上的,而不是技术上的。
主要的技术考虑是每个function只有一个返回值,但可以有任意数量的out参数。 这可以解决,但这样做可能是不可接受的。 例如,如果您想保留函数的返回值以用作许多标准库函数产生的状态代码,那么这会限制您发送回其他数据的选项。
一些风格上的考虑是
关于自最初发布此答案以来对该问题的修改,如果问题是关于是否动态分配和填充新的 object与填充调用者提出的 object ,那么还有这些额外的注意事项:
当然,你可以同时拥有它:
void init_thing(thing *t, char *name) {
t->name = name;
}
thing *create_thing(char *name) {
thing *t = new malloc(sizeof(*t));
if (t) {
init_thing(t);
}
return t;
}
两种选择都有效。
但一般情况下,通过参数返回信息(第二种方式)更可取,因为我们通常保留function的返回报错。 我们可以通过多个参数返回多个信息。 因此,调用者更容易通过首先检查返回值来检查 function 是否正常。 C 库或 Linux 系统调用中的大多数服务都是这样工作的。
关于您的示例,这两个选项都有效,因为您正在引用一个在程序加载时全局分配的常量字符串。 因此,在这两种解决方案中,您都返回此字符串的地址。
但是,如果您执行以下操作:
char *func(void) {
char buff[] = "example";
return buff;
}
您实际上将常量字符串“example”的内容复制到buff指向的 function 的堆栈区域中。 在调用者中,返回的地址不再有效,因为它指的是一个堆栈位置,可以被调用者调用的任何其他 function 重用。
让我们使用这个 function 编译一个程序:
#include <stdio.h>
char *func(void) {
char buff[] = "example";
return buff;
}
int main(void) {
char *p = func();
printf("%s\n", p);
return 0;
}
如果编译器的编译选项足够聪明,我们会得到第一个带有警告的危险信号,如下所示:
$ gcc -g bad.c -o bad
bad.c: In function 'func':
bad.c:5:11: warning: function returns address of local variable [-Wreturn-local-addr]
5 | return buff;
| ^~~~
编译器指出func()正在返回其堆栈中的本地空间地址,当 function 返回时,该地址不再有效。 这是触发此警告的编译器选项-Wreturn-local-addr
。 让我们停用此选项以删除警告:
$ gcc -g bad.c -o bad -Wno-return-local-addr
所以,现在我们有一个编译为 0 警告的程序,但这会产生误导,因为执行失败或可能触发一些不可预测的行为:
$ ./bad
Segmentation fault (core dumped)
您的第一个示例有效,因为"example"
中的 memory 不会被释放。 但是,如果您分配了本地(又名自动) memory 它会在 function 返回时自动被释放; 返回的指针将无效。
char *func() {
char buff[10];
// Copy into local memory
strcpy(buff, "example");
// buff will be deallocated after returning.
// warning: function returns address of local variable
return buff;
}
您可以使用malloc
返回动态 memory ,然后调用者必须free
。
char *func() {
char *buf = malloc(10);
strcpy(buff, "example");
return buff;
}
int main() {
char *buf = func();
puts(buf);
free(buf);
}
或者你让调用者分配 memory 并传入。
void *func(char **buff) {
// Copy a string into local memory
strcpy(buff, "example");
// buff will be deallocated after returning.
// warning: function returns address of local variable
return buff;
}
int main() {
char buf[10];
func(&buf);
puts(buf);
}
好处是调用者可以完全控制 memory。 他们可以重用现有的 memory,也可以使用本地的 memory。
缺点是调用者必须分配正确数量的 memory。 这可能会导致分配过多的 memory,也可能导致分配过少。
另一个缺点是 function 无法控制已传入的 memory。它无法增长、收缩或释放 memory。
例如,如果要将字符串转换为 integer,则可以像atoi
一样返回 integer。 int atoi( const char *str )
。
int num = atoi("42");
但是当转换失败时会发生什么? atoi
返回0
,但是如何区分atoi("0")
和atoi("purple")
?
您可以改为为转换后的值传入一个int *
。 int my_atoi( const char *str, int *ret )
。
int num;
int err = my_atoi("42", &num);
if(err) {
exit(1);
}
else {
printf("%d\n");
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.