从宏调用函数
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) == 0
和 LOG2_2(2) == 1
。 LOG2_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))
你好我正在尝试创建一个宏来计算 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) == 0
和 LOG2_2(2) == 1
。 LOG2_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))