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

C 在编译时避免可变长度堆栈数组,c,visual-studio,memory,c-preprocessor,nmake,C,Visual Studio,Memory,C Preprocessor,Nmake,我实现了一个函数,它需要一些临时堆栈空间,其大小取决于它的一个输入。这有点像可变长度堆栈内存分配,这并不总是被认为是一个好主意(例如,它不是C90或C++的一部分,在这种情况下,只有在GCC中通过扩展可用)。然而,我的情况略有不同:我确实知道在编译时会分配多少字节,只是对这个函数的几个不同调用不同,这些调用散布在我的代码库中 C99似乎可以,但这不是Visual Studio实现的,因此我的CI在Windows上运行失败 看来我有几个选择,没有一个是好的。我希望这个问题能让我相信其中一个,或者提

我实现了一个函数,它需要一些临时堆栈空间,其大小取决于它的一个输入。这有点像可变长度堆栈内存分配,这并不总是被认为是一个好主意(例如,它不是C90或C++的一部分,在这种情况下,只有在GCC中通过扩展可用)。然而,我的情况略有不同:我确实知道在编译时会分配多少字节,只是对这个函数的几个不同调用不同,这些调用散布在我的代码库中

C99似乎可以,但这不是Visual Studio实现的,因此我的CI在Windows上运行失败

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

  • 根据编译时常量在函数调用之外分配堆栈空间,否则我会将编译时常量作为参数传递,然后传递指针
  • 将我的函数转换为宏
  • 将我的函数转换为包装器宏,然后分配堆栈空间并将其传递给“real”函数(基本上结合了1和2)
  • 以某种方式说服VisualStudio这很好()
这里的目标不仅是得到一些有效且性能合理的东西,而且是可读且清晰的东西,因为这与作为其一部分的项目上下文密切相关。我应该注意到,堆上的分配在这里也不是一个选项

我怎样才能最好地处理这个问题


如果您更喜欢亲身体验真实世界的环境,。

您可以尝试同时提供:

模块.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);
过程
函数不需要知道临时表是如何分配的;调用方也可以
malloc
数组(并在调用后释放它们),或者使用VLA或
alloca
堆栈分配它们


复合文字被初始化。但是它们不能太大,因为否则会有堆栈溢出的风险,所以开销不应该太大。但这是你的决定。注意,在C中,初始化器列表不能为空,尽管GCC似乎毫无怨言地接受
(char[N]){}
。MSVC抱怨,或者至少我为它找到的在线编译器抱怨。

这闻起来像是动态堆栈内存分配,这是个坏主意。哦为什么这是一个“坏主意”?为什么堆栈会存在?我假设动态内存分配是不可能的?如果是这样的话,你的第一个选择将是最有意义的。@AndrewHenle关于这个问题的讨论无穷无尽,与这个问题无关;e、 它是标准的C99,但不是C++11的一部分,尽管许多编译器确实支持它作为扩展。我将重新制定以避免争议。@JohnBode啊,是的,这是相关的上下文。谢谢也许MSVC是这里的坏事。