C 从宏调用函数

C 从宏调用函数,c,c-preprocessor,logarithm,C,C Preprocessor,Logarithm,您好,我正在尝试创建一个宏来计算C中数字的以2为底的对数。该数字应该是一个表的大小,定义如下所示 我四处搜索,找到了这个包含base2log实现的站点 我的第一个想法是现在将宏创建为: #define SIZE 256 #define BASE2LOG (base2log(SIZE)) …但这看起来不是一个非常优雅的解决方案,因为即使BASE2LOG应该在编译时定义,它仍然需要每次在代码中出现时调用函数。我考虑过在一个全局变量中指定BASE2LOG,但我确信一定有比这个更简洁、更恰当的东西

您好,我正在尝试创建一个宏来计算C中数字的以2为底的对数。该数字应该是一个表的大小,定义如下所示

我四处搜索,找到了这个包含base2log实现的站点

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

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

有办法做到这一点吗


感谢您的时间。

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

我在其他地方也看到过类似的情况(例如,在中有一个),所以我不能相信这一点。我至少可以写下它的工作原理

// 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)   \
  ((n) < 2  ? 0 : \
   (n) < 4  ? 1 : \
   (n) < 8  ? 2 : \
   (n) < 16 ? 3 : \
   (n) < 32 ? 4 : \
   (n) < 64 ? 5 : \
   ...
#定义LOG2(n)\
((n)小于2?0:\
(n) <4?1:\
(n) <8?2:\
(n) <16?3:\
(n) <32?4:\
(n) <64?5:\
...

(顺便说一句,C99有
/
-风格的注释,以防有人抱怨。

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

我在其他地方也看到过类似的情况(例如,在中有一个),所以我不能相信这一点。我至少可以写下它是如何工作的

// 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位内最高1位的索引

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

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

宏可以以明显的方式进行泛化以处理较大的类型。按大于类型宽度的值进行移位是严格未定义的(不确定在编译器中它的可靠性如何),需要注意。安全的一种方法是添加强制转换(假设C99+):

也可能有更直接(稍微有点垃圾)的解决方案,例如

#define LOG2(n)   \
  ((n) < 2  ? 0 : \
   (n) < 4  ? 1 : \
   (n) < 8  ? 2 : \
   (n) < 16 ? 3 : \
   (n) < 32 ? 4 : \
   (n) < 64 ? 5 : \
   ...
#定义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))

另一个解决方案。如果数字超过最大值,则此解决方案将提供一个错误

#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))

考虑另一种方式:<代码>定义了Base2LoG 8 和<代码>定义大小(如果使用GC,则定义为1)。