我已经实现了一个需要一些临时堆栈空间的函数,其数量取决于其中一个输入。这有点像可变长度的堆栈内存分配,这并不总是被认为是一个好主意(例如,它不是C90或C ++的一部分,并且在该上下文中,仅通过扩展在gcc中可用)。但是,我的情况略有不同:我确实知道在编译时我将最终分配多少字节,这只是对于这个函数的几个不同调用它是不同的,洒在我的代码库周围。
C99似乎没问题,但这不是例如Visual Studio实现,因此我在Windows上运行的CI失败。
看来我有几个选择,其中没有一个是好的。我希望这个问题要么让我相信其中一个,要么提供一个更惯用的选择。
这里的目标不仅是获得有效且合理的性能,而且还具有可读性和清洁性,因为它与项目的上下文强烈对齐。我应该注意堆上的分配也不是一个选项。
我怎样才能最好地解决这个问题?
如果你更喜欢动手,现实世界的背景,here's a Github comment where I describe my specific instance of this problem。
您可以尝试同时提供:
module.h中
// Helper macro for calculating correct buffer size
#define CALC_SIZE(quantity) (/* expands to integer constant expression */)
// C90 compatible function
void func(uint8_t * data, int quantity);
// Optional function for newer compilers
// uses CALC_SIZE internally for simpler API similarly to 'userFunc' below
#if NOT_ANCIENT_COMPILER
void funcVLA(int quantity);
#endif
user.c的
#include "module.h"
void userFunc(void) {
uint8_t buffer[CALC_SIZE(MY_QUANTITY)];
func(buffer, MY_QUANTITY);
}
显然MSVC确实处理C99复合文字(§6.5.2.5),因此您可以将堆栈分配的数组直接传递给被调用函数作为附加参数。您可能希望使用宏来简化调用语法。
这是一个例子:
/* Function which needs two temporary arrays. Both arrays and the size
* are passed as arguments
*/
int process(const char* data, size_t n_elems, char* stack, int* height) {
/* do the work */
}
/* To call the function with n_elems known at compile-time */
int result = process(data, N, (char[N]){0}, (int[N]){0});
/* Or you might use a macro like this: */
#define process_FIXED(D, N) (process(D, N, (char[N]){0}, (int[N]){0})))
int result = process_FIXED(data, N);
process
函数不需要知道如何分配临时值;调用者也可以malloc
数组(并在调用后释放它们)或使用VLA或alloca
进行堆栈分配。
初始化复合文字。但是它们不能太大,否则你冒着堆栈溢出的风险,所以开销不应该过大。但那是你的电话。请注意,在C中,初始化列表不能为空,尽管GCC似乎接受(char[N]){}
而没有抱怨。 MSVC抱怨,或者至少我找到的在线编译器抱怨。