我经常使用便捷函数返回指向静态缓冲区的指针,如下所示:
char* p(int x) {
static char res[512];
snprintf(res, sizeof(res)-1, "number is %d", x));
return res;
}
并将它们作为其他函数的参数使用:
...
some_func( somearg, p(6) );
....
然而,这种“便利性”除了不是线程安全之外还有一个恼人的缺点(可能还有更多原因):
some_func( somearg, p(6), p(7) );
上面显然没有做我想要的,因为最后两个参数将指向相同的内存空间。我希望能够在没有太多麻烦的情况下正常工作。
所以我的问题是:
我是否有一些神奇的方法让我无法完成我想要的事情,而无需进行繁琐的分配和释放吗
*****更新2010-04-20 *****
无耻的插件:看看我自己的答案here
我想它会起作用,但它也接近于矫枉过正。意见?
答案 0 :(得分:8)
嗯,一种广泛使用的方法是在调用者上为结果准备内存缓冲区。呼叫者可以选择最喜欢的方法。
在您的情况下,将p
写为
char* p(char *buffer, size_t max_length, int x) {
snprintf(buffer, max_length, "number is %d", x);
return buffer;
}
并将其命名为
char buffer1[512], buffer2[512];
some_func( somearg, p(buffer1, sizeof buffer1 - 1, 6), p(buffer2, sizeof buffer2 - 1, 7) );
这种方法至少有一个明显的缺点:一般情况下,调用者事先并不知道需要为缓冲区分配多少个字符。如果一个好的常量编译时值可用,则很容易,但在更复杂的情况下需要额外的工作,比如提供某种“预计算”功能,将所需的缓冲区大小作为运行时值返回。 (snprintf
函数实际上是这样工作的:您可以使用空缓冲区指针和零缓冲区大小调用它,只是为了确定缓冲区大小而进行虚构运行。
答案 1 :(得分:2)
让调用者提供缓冲区(以及缓冲区的大小)。它是线程安全的,缓冲区通常可以放在堆栈上,因此不会带来堆分配的开销。
答案 2 :(得分:2)
只要您理解这是线程不安全的,并且您的逻辑期望“方便”方法返回的值仅在少数调用[[]的持续时间内有效。可能是各种各样的方法,你可以用两种不相关和可能互补的方式来扩展这种便利。
为方便方法添加一个额外的参数,以便这些方法可以传递-optionally-返回值的容器(如果不能隐式设置这样的大小,也可以添加size_of_passed_buffer参数)。每当调用者提供缓冲区时,请使用它,否则,请使用静态缓冲区 顺便说一句,如果传递给方便方法的缓冲区是局部变量,它们的分配将自动(并且充分)地管理,遵循调用便捷方法的子程序的生命周期。
使用循环缓冲区,在重用缓冲区元素之前允许给定数量的调用 这样的缓冲区也可以是全局的,即与多个“方便”方法共享(当然也需要a)在线程方面运行良好,b)共享指向缓冲区中下一个可用字节/元素的指针。
实现所有这些似乎是
但是,只要
这种模式提供了一个简单的自动堆管理系统,这在C中是一件好事(与Java不同,.NET和其他系统不提供内置的基于GC的堆管理)
答案 3 :(得分:0)
简而言之,没有。 C不提供任何形式的自动堆管理,因此您可以自己跟踪已分配的内存。标准的类C解决方案是让调用者提供缓冲区,而不是在内部分配缓冲区。虽然这只是追踪内存的责任,但它往往会在一个更方便的地方结束。我想,如果你想在C中获得一种垃圾收集形式,你可以查看Boehm's conservative garbage collector。
答案 4 :(得分:0)
如果助手的参数始终是文字(如示例所示),则可以使用宏:
#define P(NUMLIT) ("number is " #NUMLIT)
...
somefunc(somearg, P(6), P(7));
...
预处理器从宏参数NUMLIT创建一个字符串,并将其附加到“number is”以创建单个字符串文字,就像
一样"this" " " "string"
作为单个字符串文字发出,“this string”。
答案 5 :(得分:0)
我找到了另一种方法。像这样:
#define INIT(n) \
int xi = 0; \
char *x[n]; \
#define MACRO(s) \
(++xi, xi %= sizeof(x)/sizeof(*x), x[xi] = alloca(strlen(s)+1), strcpy(x[xi], (s)), x[xi])
我可以这样打电话:
INIT(2);
some_func( somearg, MACRO("testing1"), MACRO("testing2"));
因此缓冲区在堆栈中而不需要任何释放。它甚至是线程安全的。