可以在 C 预处理器步骤中调用函数或计算吗?

It is possible to call function or calculate in C preprocessor step?

我想创建长度为(int)log(macro constant)的数组 喜欢

#define MACRO_NUM 100
int arr[(int)log(MACRO_NUM)];

但是如您所知,MSVC 不支持变长数组。因此,声明数组时,我不能在数组索引处使用变量或函数。 这意味着在 运行 时间内确定的每个值都不能成为数组的索引。所以我认为如果可能的话,预处理器应该在编译之前做一些计算。

所以我的问题是

  1. 我的推断对吗?
  2. 那么有没有办法让预处理器计算呢?或者预处理器只能做替换。

我知道还有另一种方法,例如使用指针动态分配等,但是,作为一名学生,我只是徘徊,当不支持可变长度数组时,可以使用数组和宏常量来执行此操作。

不,预处理器不进行计算,只进行替换和其他常见的预处理器内容。

但是,有时您可以获得类似如下的效果。在 C 中几乎任何需要编译时常量的地方,您都可以使用任意算术表达式,其操作数是常量(将由编译器计算,而不是预处理器)。宏可以是包装此类表达式的便捷方式,特别是如果它会很长。

作为一个简单的例子,你可以这样做

#define SQUARE(x) ((x)*(x))
int foo[SQUARE(6)];

经过预处理后,这就是int foo[((6)*(6))];,编译器定义了一个长度为36的数组。计算是由编译器完成的,而不是预处理器,但效果是一样的。

现在 log 通常不是编译器可以在编译时计算的函数,但您可以伪造它:

#define MY_LOG(x) ((x) < 10 ? 0 : (x) < 100 ? 1 : (x) < 1000 ? 2 : .....) 

根据需要添加更多 ?:,直到用完 intlong 或其他任何范围。对于以十为底的对数,它最多只能是 10 或 20。然后你可以做

#define MACRO_NUM 100
int arr[MY_LOG(MACRO_NUM)]

并得到一个长度为 2 的数组。

请注意不要将 MY_LOG 与有副作用的参数一起使用!

如果 MACRO_NUM 保证是一个编译时常量(例如 100)并且 (int)log(MACRO_NUM) 你的意思是“最大的 k 这样2k ≤ MACRO_NUM", 则可以完成(但不是通过让预处理器进行计算,这不是真的有可能):

注意:请参阅下面的更紧凑的版本。

#define LOG_MACRO_NUM ((MACRO_NUM >= (1UL<<1)) + \
                       (MACRO_NUM >= (1UL<<2)) + \
                       (MACRO_NUM >= (1UL<<3)) + \
                       (MACRO_NUM >= (1UL<<4)) + \
                       (MACRO_NUM >= (1UL<<5)) + \
                       (MACRO_NUM >= (1UL<<6)) + \
                       (MACRO_NUM >= (1UL<<7)) + \
                       (MACRO_NUM >= (1UL<<8)) + \
                       (MACRO_NUM >= (1UL<<9)) + \
                       (MACRO_NUM >= (1UL<<10)) + \
                       (MACRO_NUM >= (1UL<<11)) + \
                       (MACRO_NUM >= (1UL<<12)) + \
                       (MACRO_NUM >= (1UL<<13)) + \
                       (MACRO_NUM >= (1UL<<14)) + \
                       (MACRO_NUM >= (1UL<<15)) + \
                       (MACRO_NUM >= (1UL<<16)) + \
                       (MACRO_NUM >= (1UL<<17)) + \
                       (MACRO_NUM >= (1UL<<18)) + \
                       (MACRO_NUM >= (1UL<<19)) + \
                       (MACRO_NUM >= (1UL<<20)) + \
                       (MACRO_NUM >= (1UL<<21)) + \
                       (MACRO_NUM >= (1UL<<22)) + \
                       (MACRO_NUM >= (1UL<<23)) + \
                       (MACRO_NUM >= (1UL<<24)) + \
                       (MACRO_NUM >= (1UL<<25)) + \
                       (MACRO_NUM >= (1UL<<26)) + \
                       (MACRO_NUM >= (1UL<<27)) + \
                       (MACRO_NUM >= (1UL<<28)) + \
                       (MACRO_NUM >= (1UL<<29)) + \
                       (MACRO_NUM >= (1UL<<30)) + \
                       (MACRO_NUM >= (1UL<<31)))
/* If you want to handle 64-bit numbers, change 1UL to 1ULL
 * and continue the pattern up to 63.
 */

Live on coliru

这可以使用 Boost preprocessor library 变得更加紧凑,这与大多数 Boost 同时使用 C 和 C++ 不同。它是一个单独的头文件库,因此只需复制头文件目录即可独立安装。将上面的丑陋减少到:

#include <stdio.h>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#define CHECK_POWER(Z,I,V) + ((V) >= (1ULL << I))
#define ILOGB(C) (BOOST_PP_REPEAT_FROM_TO(1, 32, CHECK_POWER, C))

/* Declared at file scope to show that the macro produces a
 * compile-time constant; static VLAs are not allowed
 * even on compilers which implement VLAs.
 */
int arr[ILOGB(4127)] = {0};
int main(void) {
    printf("array has %zu elements.\n", sizeof arr / sizeof *arr);
}

Live on coliru