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

问题描述 投票:2回答:2

我已经实现了一个需要一些临时堆栈空间的函数,其数量取决于其中一个输入。这有点像可变长度的堆栈内存分配,这并不总是被认为是一个好主意(例如,它不是C90或C ++的一部分,并且在该上下文中,仅通过扩展在gcc中可用)。但是,我的情况略有不同:我确实知道在编译时我将最终分配多少字节,这只是对于这个函数的几个不同调用它是不同的,洒在我的代码库周围。

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

看来我有几个选择,其中没有一个是好的。我希望这个问题要么让我相信其中一个,要么提供一个更惯用的选择。

  • 根据我作为参数传递的编译时常量,在函数调用之外分配堆栈空间,然后传递指针。
  • 把我的功能变成一个宏。
  • 将我的函数转换为包装器宏,然后分配堆栈空间并将其传递给“真实”函数(基本上组合1和2)。
  • 不知何故说服Visual Studio这很好(relevant NMakefile)。

这里的目标不仅是获得有效且合理的性能,而且还具有可读性和清洁性,因为它与项目的上下文强烈对齐。我应该注意堆上的分配也不是一个选项。

我怎样才能最好地解决这个问题?

如果你更喜欢动手,现实世界的背景,here's a Github comment where I describe my specific instance of this problem

c visual-studio memory c-preprocessor nmake
2个回答
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);
}

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

© www.soinside.com 2019 - 2024. All rights reserved.