避免在编译时使用可变长度堆栈数组

时间:2019-04-08 11:27:16

标签: c visual-studio memory c-preprocessor nmake

我实现了一个函数,该函数需要一些临时堆栈空间,该堆栈空间的数量取决于其输入之一。闻起来像是可变长度堆栈内存分配,这并不总是一个好主意(例如,它不是C90或C ++的一部分,并且在这种情况下,只能通过扩展名在gcc中使用)。但是,我的情况略有不同:我确实知道最终在编译时会分配多少个字节,只是对这个函数的几次不同调用都有所不同,遍及我的代码库。

C99似乎可以解决这个问题,但这不是例如Visual Studio实现了,因此我的CI在Windows上运行失败。

看来我有几个选择,但都不是个好选择。我希望这个问题可以使我相信其中之一,或者提供一种更惯用的选择。

这里的目标不仅是要获得可以正常工作且性能合理的产品,而且还应使它们易于阅读和清晰,因为它与项目的内容紧密相关。这是其中的一部分。我应该注意,在这里也不可以在堆上进行分配。

我该如何最好地处理?

如果您希望获得更多动手的真实环境,请here's a Github comment where I describe my specific instance of this problem

2 个答案:

答案 0 :(得分:0)

您可以尝试同时提供:

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);
}

答案 1 :(得分:0)

显然,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抱怨,或者至少我为此找到的在线编译器抱怨。

相关问题
最新问题