从宏调用函数

Calling a function from a macro

你好我正在尝试创建一个宏来计算 C 中数字的以 2 为底的对数。该数字应该是 table 的大小,也就是 #defined 也如下所示。

我搜索了一下,发现这个网站包含 base2log 的实现 https://graphics.stanford.edu/~seander/bithacks.html

uint8_t base2log(uint16_t value){

    uint8_t result = 0; // r will be lg(v)

    while (value >>= 1)
    {
        result++;
    }
    return result;
}

我的第一个想法是现在将宏创建为:

#define SIZE 256
#define BASE2LOG (base2log(SIZE))

...但这看起来不是一个非常优雅的解决方案,因为即使 BASE2LOG 应该在编译时定义,它仍然需要在代码中每次出现时调用一个函数。我考虑过在全局变量中分配 BASE2LOG,但我确信一定有比这更简洁和适当的东西。

有办法吗?

感谢您的宝贵时间。

这是一个纯宏解决方案,它允许在编译时计算对数,并在 C 中需要 整数常量表达式 的地方使用——例如,当指定长度一个(非可变长度)数组。 整数常量表达式 只是编译器必须能够在编译时求值的表达式的标准术语。

我在其他地方看到过这种变体(例如 Macro to compute number of bits needed to store a number n 中有一个),所以我不能相信。我至少可以写下它是如何工作的。

// Computes the base 2 logarithm of a 16-bit number. Meant to be used
// at compile time.
#define LOG2(n)   ((n) & 0xFF00 ? 8 + LOG2_8((n) >> 8) : LOG2_8(n))
#define LOG2_8(n) ((n) & 0xF0   ? 4 + LOG2_4((n) >> 4) : LOG2_4(n))
#define LOG2_4(n) ((n) & 0xC    ? 2 + LOG2_2((n) >> 2) : LOG2_2(n))
#define LOG2_2(n) ((n) & 0x2    ? 1 : 0)

一个数截断的以2为底的对数就是最高1位的索引(0是最低位的索引)。要找到最高 1 位,可以使用一种二进制搜索。

LOG2()(主宏)中,我们使用(n) & 0xFF00 ? ...来测试高字节是否包含1位。如果是,则n中最高1位的索引为8加上n高字节中最高1位的索引。如果不是,那么最高1位的索引就是低字节中最高1位的索引。

为了得到高字节,我们做(n) >> 8。剩下的宏只看低字节,不用屏蔽

LOG2_8() 宏计算一个字节中最高 1 位的索引。它使用与上述相同的逻辑,间隔减半。如果高4位包含一个1位,则最高1位的索引为4加上高4位内最高1位的索引。否则为低4位中最高位的索引。

LOG_4() 的工作方式完全相同。

对于基本情况,LOG2_2(1) == 0LOG2_2(2) == 1LOG2_2(0)(数学上未定义)也恰好变为 0。

宏可以被概括为以显而易见的方式处理更大的类型。移动一个大于类型宽度的值是严格未定义的(不确定它在实践中跨编译器的可靠性如何)并且需要注意。使其安全的一种方法是添加一个强制转换(假设为 C99+):

#define LOG2(n)    LOG2_64((uint64_t)(n))
#define LOG2_64(n) ... /* as usual. */
...

更直接(和稍微垃圾邮件更多)的解决方案也是可能的,例如

#define LOG2(n)   \
  ((n) < 2  ? 0 : \
   (n) < 4  ? 1 : \
   (n) < 8  ? 2 : \
   (n) < 16 ? 3 : \
   (n) < 32 ? 4 : \
   (n) < 64 ? 5 : \
   ...

(顺便说一下,C99 有 // 风格的评论,以防有人想抱怨。;)

另一种解决方案。如果数字超过最大值,这个会报错。

#define MIN_BITS(n) (1+(n>1)+(n>3)+(n>7)+(n>0x0f)+(n>0x1f)+(n>0x3f)+(n>0x7f)+   \
                    (n>0x0ff)+(n>0x1ff)+(n>0x3ff)+(n>0x7ff)+                    \
                    (n>0xfff)+(n>0x1fff)+(n>0x3fff)+(n>0x7fff)+                \
                    ((n>0xffff)?100:0))